Merge "Removing tablet-specific assets." into jb-ub-now-jetsonic
diff --git a/res/layout/wallpaper_cropper.xml b/res/layout/wallpaper_cropper.xml
index 3a3d98a..abb8608 100644
--- a/res/layout/wallpaper_cropper.xml
+++ b/res/layout/wallpaper_cropper.xml
@@ -32,7 +32,7 @@
         android:visibility="invisible"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center"
+        android:layout_centerInParent="true"
         android:indeterminate="true"
         android:indeterminateOnly="true"
         android:background="@android:color/transparent" />
diff --git a/res/layout/wallpaper_picker.xml b/res/layout/wallpaper_picker.xml
index 0492b7b..620ce1f 100644
--- a/res/layout/wallpaper_picker.xml
+++ b/res/layout/wallpaper_picker.xml
@@ -38,7 +38,7 @@
         android:visibility="invisible"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center"
+        android:layout_centerInParent="true"
         android:indeterminate="true"
         android:indeterminateOnly="true"
         android:background="@android:color/transparent" />
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index 01f72a7..5d4f9c6 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -30,6 +30,7 @@
     static HashSet<Animator> sAnimators = new HashSet<Animator>();
     static Animator.AnimatorListener sEndAnimListener = new Animator.AnimatorListener() {
         public void onAnimationStart(Animator animation) {
+            sAnimators.add(animation);
         }
 
         public void onAnimationRepeat(Animator animation) {
@@ -45,7 +46,6 @@
     };
 
     public static void cancelOnDestroyActivity(Animator a) {
-        sAnimators.add(a);
         a.addListener(sEndAnimListener);
     }
 
diff --git a/src/com/android/launcher3/SavedWallpaperImages.java b/src/com/android/launcher3/SavedWallpaperImages.java
index c19692b..086d085 100644
--- a/src/com/android/launcher3/SavedWallpaperImages.java
+++ b/src/com/android/launcher3/SavedWallpaperImages.java
@@ -60,13 +60,9 @@
         public void onClick(WallpaperPickerActivity a) {
             String imageFilename = a.getSavedImages().getImageFilename(mDbId);
             File file = new File(a.getFilesDir(), imageFilename);
-            CropView v = a.getCropView();
-            int rotation = WallpaperCropActivity.getRotationFromExif(file.getAbsolutePath());
-            a.getDefaultWallpaperView().setVisibility(View.INVISIBLE);
-            v.setTileSource(
-                    new BitmapRegionTileSource(a, file.getAbsolutePath(), 1024, rotation), null);
-            v.moveToLeft();
-            v.setTouchEnabled(false);
+            BitmapRegionTileSource.FilePathBitmapSource bitmapSource =
+                    new BitmapRegionTileSource.FilePathBitmapSource(file.getAbsolutePath(), 1024);
+            a.setCropViewTileSource(bitmapSource, false, true);
         }
         @Override
         public void onSave(WallpaperPickerActivity a) {
diff --git a/src/com/android/launcher3/WallpaperCropActivity.java b/src/com/android/launcher3/WallpaperCropActivity.java
index 30ec340..29e8c97 100644
--- a/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/src/com/android/launcher3/WallpaperCropActivity.java
@@ -37,7 +37,6 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
-import android.util.FloatMath;
 import android.util.Log;
 import android.view.Display;
 import android.view.View;
@@ -96,9 +95,6 @@
             return;
         }
 
-        int rotation = getRotationFromExif(this, imageUri);
-        mCropView.setTileSource(new BitmapRegionTileSource(this, imageUri, 1024, rotation), null);
-        mCropView.setTouchEnabled(true);
         // Action bar
         // Show the custom action bar view
         final ActionBar actionBar = getActionBar();
@@ -111,6 +107,46 @@
                         cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
                     }
                 });
+
+        // Load image in background
+        setCropViewTileSource(
+                new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024), true, false);
+    }
+
+    public void setCropViewTileSource(final BitmapRegionTileSource.BitmapSource bitmapSource,
+            final boolean touchEnabled, final boolean moveToLeft) {
+        final Context context = WallpaperCropActivity.this;
+        final View progressView = findViewById(R.id.loading);
+        final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
+            protected Void doInBackground(Void...args) {
+                if (!isCancelled()) {
+                    bitmapSource.loadInBackground();
+                }
+                return null;
+            }
+            protected void onPostExecute(Void arg) {
+                if (!isCancelled()) {
+                    progressView.setVisibility(View.INVISIBLE);
+                    mCropView.setTileSource(
+                            new BitmapRegionTileSource(context, bitmapSource), null);
+                    mCropView.setTouchEnabled(touchEnabled);
+                    if (moveToLeft) {
+                        mCropView.moveToLeft();
+                    }
+                }
+            }
+        };
+        // We don't want to show the spinner every time we load an image, because that would be
+        // annoying; instead, only start showing the spinner if loading the image has taken
+        // longer than 1 sec (ie 1000 ms)
+        progressView.postDelayed(new Runnable() {
+            public void run() {
+                if (loadBitmapTask.getStatus() != AsyncTask.Status.FINISHED) {
+                    progressView.setVisibility(View.VISIBLE);
+                }
+            }
+        }, 1000);
+        loadBitmapTask.execute();
     }
 
     public boolean enableRotation() {
diff --git a/src/com/android/launcher3/WallpaperPickerActivity.java b/src/com/android/launcher3/WallpaperPickerActivity.java
index 2ad9218..9c6ee6e 100644
--- a/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -42,6 +42,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LevelListDrawable;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
 import android.provider.MediaStore;
@@ -133,11 +134,8 @@
         }
         @Override
         public void onClick(WallpaperPickerActivity a) {
-            CropView v = a.getCropView();
-            int rotation = WallpaperCropActivity.getRotationFromExif(a, mUri);
-            a.getDefaultWallpaperView().setVisibility(View.INVISIBLE);
-            v.setTileSource(new BitmapRegionTileSource(a, mUri, 1024, rotation), null);
-            v.setTouchEnabled(true);
+            a.setCropViewTileSource(
+                    new BitmapRegionTileSource.UriBitmapSource(a, mUri, 1024), true, false);
         }
         @Override
         public void onSave(final WallpaperPickerActivity a) {
@@ -175,9 +173,10 @@
         }
         @Override
         public void onClick(WallpaperPickerActivity a) {
-            int rotation = WallpaperCropActivity.getRotationFromExif(mResources, mResId);
-            BitmapRegionTileSource source = new BitmapRegionTileSource(
-                    mResources, a, mResId, 1024, rotation);
+            BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
+                    new BitmapRegionTileSource.ResourceBitmapSource(mResources, mResId, 1024);
+            bitmapSource.loadInBackground();
+            BitmapRegionTileSource source = new BitmapRegionTileSource(a, bitmapSource);
             CropView v = a.getCropView();
             a.getDefaultWallpaperView().setVisibility(View.INVISIBLE);
             v.setTileSource(source, null);
@@ -528,6 +527,12 @@
             }
         };
     }
+    @Override
+    public void setCropViewTileSource(final BitmapRegionTileSource.BitmapSource bitmapSource,
+            final boolean touchEnabled, boolean moveToLeft) {
+        getDefaultWallpaperView().setVisibility(View.INVISIBLE);
+        super.setCropViewTileSource(bitmapSource, touchEnabled, moveToLeft);
+    }
 
     private void initializeScrollForRtl() {
         final HorizontalScrollView scroll =
@@ -687,26 +692,34 @@
         }
     }
 
-    private void addTemporaryWallpaperTile(Uri uri) {
+    private void addTemporaryWallpaperTile(final Uri uri) {
         mTempWallpaperTiles.add(uri);
         // Add a tile for the image picked from Gallery
         FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
                 inflate(R.layout.wallpaper_picker_item, mWallpapersView, false);
         setWallpaperItemPaddingToZero(pickedImageThumbnail);
+        mWallpapersView.addView(pickedImageThumbnail, 0);
 
         // Load the thumbnail
-        ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
-        Point defaultSize = getDefaultThumbnailSize(this.getResources());
-        int rotation = WallpaperCropActivity.getRotationFromExif(this, uri);
-        Bitmap thumb = createThumbnail(defaultSize, this, uri, null, null, 0, rotation, false);
-        if (thumb != null) {
-            image.setImageBitmap(thumb);
-            Drawable thumbDrawable = image.getDrawable();
-            thumbDrawable.setDither(true);
-        } else {
-            Log.e(TAG, "Error loading thumbnail for uri=" + uri);
-        }
-        mWallpapersView.addView(pickedImageThumbnail, 0);
+        final ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
+        final Point defaultSize = getDefaultThumbnailSize(this.getResources());
+        final Context context = this;
+        new AsyncTask<Void, Bitmap, Bitmap>() {
+            protected Bitmap doInBackground(Void...args) {
+                int rotation = WallpaperCropActivity.getRotationFromExif(context, uri);
+                return createThumbnail(defaultSize, context, uri, null, null, 0, rotation, false);
+
+            }
+            protected void onPostExecute(Bitmap thumb) {
+                if (thumb != null) {
+                    image.setImageBitmap(thumb);
+                    Drawable thumbDrawable = image.getDrawable();
+                    thumbDrawable.setDither(true);
+                } else {
+                    Log.e(TAG, "Error loading thumbnail for uri=" + uri);
+                }
+            }
+        }.execute();
 
         UriWallpaperInfo info = new UriWallpaperInfo(uri);
         pickedImageThumbnail.setTag(info);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index a1353c3..e9d41d5 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -4085,6 +4085,7 @@
             }
         }
         mRestoredPages.clear();
+        mSavedStates = null;
     }
 
     @Override
diff --git a/src/com/android/photos/BitmapRegionTileSource.java b/src/com/android/photos/BitmapRegionTileSource.java
index 5f64018..74284b2 100644
--- a/src/com/android/photos/BitmapRegionTileSource.java
+++ b/src/com/android/photos/BitmapRegionTileSource.java
@@ -31,11 +31,13 @@
 import android.util.Log;
 
 import com.android.gallery3d.common.BitmapUtils;
+import com.android.gallery3d.exif.ExifInterface;
 import com.android.gallery3d.glrenderer.BasicTexture;
 import com.android.gallery3d.glrenderer.BitmapTexture;
 import com.android.photos.views.TiledImageRenderer;
 
 import java.io.BufferedInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -55,6 +57,175 @@
     // due to decodePreview being allowed to be up to 2x the size of the target
     private static final int MAX_PREVIEW_SIZE = 1024;
 
+    public static abstract class BitmapSource {
+        private BitmapRegionDecoder mDecoder;
+        private Bitmap mPreview;
+        private int mPreviewSize;
+        private int mRotation;
+        public BitmapSource(int previewSize) {
+            mPreviewSize = previewSize;
+        }
+        public void loadInBackground() {
+            ExifInterface ei = new ExifInterface();
+            if (readExif(ei)) {
+                Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
+                if (ori != null) {
+                    mRotation = ExifInterface.getRotationForOrientationValue(ori.shortValue());
+                }
+            }
+            mDecoder = loadBitmapRegionDecoder();
+            int width = mDecoder.getWidth();
+            int height = mDecoder.getHeight();
+            if (mPreviewSize != 0) {
+                int previewSize = Math.min(mPreviewSize, MAX_PREVIEW_SIZE);
+                BitmapFactory.Options opts = new BitmapFactory.Options();
+                opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
+                opts.inPreferQualityOverSpeed = true;
+
+                float scale = (float) previewSize / Math.max(width, height);
+                opts.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
+                opts.inJustDecodeBounds = false;
+                mPreview = loadPreviewBitmap(opts);
+            }
+        }
+
+        public BitmapRegionDecoder getBitmapRegionDecoder() {
+            return mDecoder;
+        }
+
+        public Bitmap getPreviewBitmap() {
+            return mPreview;
+        }
+
+        public int getPreviewSize() {
+            return mPreviewSize;
+        }
+
+        public int getRotation() {
+            return mRotation;
+        }
+
+        public abstract boolean readExif(ExifInterface ei);
+        public abstract BitmapRegionDecoder loadBitmapRegionDecoder();
+        public abstract Bitmap loadPreviewBitmap(BitmapFactory.Options options);
+    }
+
+    public static class FilePathBitmapSource extends BitmapSource {
+        private String mPath;
+        public FilePathBitmapSource(String path, int previewSize) {
+            super(previewSize);
+            mPath = path;
+        }
+        @Override
+        public BitmapRegionDecoder loadBitmapRegionDecoder() {
+            try {
+                return BitmapRegionDecoder.newInstance(mPath, true);
+            } catch (IOException e) {
+                Log.w("BitmapRegionTileSource", "getting decoder failed", e);
+                return null;
+            }
+        }
+        @Override
+        public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
+            return BitmapFactory.decodeFile(mPath, options);
+        }
+        @Override
+        public boolean readExif(ExifInterface ei) {
+            try {
+                ei.readExif(mPath);
+                return true;
+            } catch (IOException e) {
+                Log.w("BitmapRegionTileSource", "getting decoder failed", e);
+                return false;
+            }
+        }
+    }
+
+    public static class UriBitmapSource extends BitmapSource {
+        private Context mContext;
+        private Uri mUri;
+        public UriBitmapSource(Context context, Uri uri, int previewSize) {
+            super(previewSize);
+            mContext = context;
+            mUri = uri;
+        }
+        private InputStream regenerateInputStream() throws FileNotFoundException {
+            InputStream is = mContext.getContentResolver().openInputStream(mUri);
+            return new BufferedInputStream(is);
+        }
+        @Override
+        public BitmapRegionDecoder loadBitmapRegionDecoder() {
+            try {
+                return BitmapRegionDecoder.newInstance(regenerateInputStream(), true);
+            } catch (FileNotFoundException e) {
+                Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
+                return null;
+            } catch (IOException e) {
+                Log.e("BitmapRegionTileSource", "Failure while reading URI " + mUri, e);
+                return null;
+            }
+        }
+        @Override
+        public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
+            try {
+                return BitmapFactory.decodeStream(regenerateInputStream(), null, options);
+            } catch (FileNotFoundException e) {
+                Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
+                return null;
+            }
+        }
+        @Override
+        public boolean readExif(ExifInterface ei) {
+            try {
+                ei.readExif(regenerateInputStream());
+                return true;
+            } catch (FileNotFoundException e) {
+                Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
+                return false;
+            } catch (IOException e) {
+                Log.e("BitmapRegionTileSource", "Failure while reading URI " + mUri, e);
+                return false;
+            }
+        }
+    }
+
+    public static class ResourceBitmapSource extends BitmapSource {
+        private Resources mRes;
+        private int mResId;
+        public ResourceBitmapSource(Resources res, int resId, int previewSize) {
+            super(previewSize);
+            mRes = res;
+            mResId = resId;
+        }
+        private InputStream regenerateInputStream() {
+            InputStream is = mRes.openRawResource(mResId);
+            return new BufferedInputStream(is);
+        }
+        @Override
+        public BitmapRegionDecoder loadBitmapRegionDecoder() {
+            try {
+                return BitmapRegionDecoder.newInstance(regenerateInputStream(), true);
+            } catch (IOException e) {
+                Log.e("BitmapRegionTileSource", "Error reading resource", e);
+                return null;
+            }
+        }
+        @Override
+        public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
+            return BitmapFactory.decodeResource(mRes, mResId, options);
+        }
+        @Override
+        public boolean readExif(ExifInterface ei) {
+            try {
+                ei.readExif(regenerateInputStream());
+                return true;
+            } catch (IOException e) {
+                Log.e("BitmapRegionTileSource", "Error reading resource", e);
+                return false;
+            }
+        }
+    }
+
     BitmapRegionDecoder mDecoder;
     int mWidth;
     int mHeight;
@@ -68,50 +239,23 @@
     private BitmapFactory.Options mOptions;
     private Canvas mCanvas;
 
-    public BitmapRegionTileSource(Context context, String path, int previewSize, int rotation) {
-        this(null, context, path, null, 0, previewSize, rotation);
-    }
-
-    public BitmapRegionTileSource(Context context, Uri uri, int previewSize, int rotation) {
-        this(null, context, null, uri, 0, previewSize, rotation);
-    }
-
-    public BitmapRegionTileSource(Resources res,
-            Context context, int resId, int previewSize, int rotation) {
-        this(res, context, null, null, resId, previewSize, rotation);
-    }
-
-    private BitmapRegionTileSource(Resources res,
-            Context context, String path, Uri uri, int resId, int previewSize, int rotation) {
+    public BitmapRegionTileSource(Context context, BitmapSource source) {
         mTileSize = TiledImageRenderer.suggestedTileSize(context);
-        mRotation = rotation;
-        try {
-            if (path != null) {
-                mDecoder = BitmapRegionDecoder.newInstance(path, true);
-            } else if (uri != null) {
-                InputStream is = context.getContentResolver().openInputStream(uri);
-                BufferedInputStream bis = new BufferedInputStream(is);
-                mDecoder = BitmapRegionDecoder.newInstance(bis, true);
-            } else {
-                InputStream is = res.openRawResource(resId);
-                BufferedInputStream bis = new BufferedInputStream(is);
-                mDecoder = BitmapRegionDecoder.newInstance(bis, true);
-            }
-            mWidth = mDecoder.getWidth();
-            mHeight = mDecoder.getHeight();
-        } catch (IOException e) {
-            Log.w("BitmapRegionTileSource", "ctor failed", e);
-        }
+        mRotation = source.getRotation();
+        mDecoder = source.getBitmapRegionDecoder();
+        mWidth = mDecoder.getWidth();
+        mHeight = mDecoder.getHeight();
         mOptions = new BitmapFactory.Options();
         mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
         mOptions.inPreferQualityOverSpeed = true;
         mOptions.inTempStorage = new byte[16 * 1024];
+        int previewSize = source.getPreviewSize();
         if (previewSize != 0) {
             previewSize = Math.min(previewSize, MAX_PREVIEW_SIZE);
             // Although this is the same size as the Bitmap that is likely already
             // loaded, the lifecycle is different and interactions are on a different
             // thread. Thus to simplify, this source will decode its own bitmap.
-            Bitmap preview = decodePreview(res, context, path, uri, resId, previewSize);
+            Bitmap preview = decodePreview(source, previewSize);
             if (preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) {
                 mPreview = new BitmapTexture(preview);
             } else {
@@ -215,33 +359,15 @@
      * Note that the returned bitmap may have a long edge that's longer
      * than the targetSize, but it will always be less than 2x the targetSize
      */
-    private Bitmap decodePreview(
-            Resources res, Context context, String file, Uri uri, int resId, int targetSize) {
-        float scale = (float) targetSize / Math.max(mWidth, mHeight);
-        mOptions.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
-        mOptions.inJustDecodeBounds = false;
-
-        Bitmap result = null;
-        if (file != null) {
-            result = BitmapFactory.decodeFile(file, mOptions);
-        } else if (uri != null) {
-            try {
-                InputStream is = context.getContentResolver().openInputStream(uri);
-                BufferedInputStream bis = new BufferedInputStream(is);
-                result = BitmapFactory.decodeStream(bis, null, mOptions);
-            } catch (IOException e) {
-                Log.w("BitmapRegionTileSource", "getting preview failed", e);
-            }
-        } else {
-            result = BitmapFactory.decodeResource(res, resId, mOptions);
-        }
+    private Bitmap decodePreview(BitmapSource source, int targetSize) {
+        Bitmap result = source.getPreviewBitmap();
         if (result == null) {
             return null;
         }
 
         // We need to resize down if the decoder does not support inSampleSize
         // or didn't support the specified inSampleSize (some decoders only do powers of 2)
-        scale = (float) targetSize / (float) (Math.max(result.getWidth(), result.getHeight()));
+        float scale = (float) targetSize / (float) (Math.max(result.getWidth(), result.getHeight()));
 
         if (scale <= 0.5) {
             result = BitmapUtils.resizeBitmapByScale(result, scale, true);