Passing the full image and crop hint when setting the wallpaper

Bug: 25454157
Change-Id: Ia0ae7dd4963b72cd7902622847deedfcb5a0eca2
diff --git a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java b/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
index 027e947..153fa90 100644
--- a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
+++ b/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
@@ -47,7 +47,7 @@
 public class BitmapCropTask extends AsyncTask<Integer, Void, Boolean> {
 
     public interface OnBitmapCroppedHandler {
-        public void onBitmapCropped(byte[] imageBytes);
+        public void onBitmapCropped(byte[] imageBytes, Rect cropHint);
     }
 
     public interface OnEndCropHandler {
@@ -171,29 +171,42 @@
     public boolean cropBitmap(int whichWallpaper) {
         boolean failure = false;
 
-
-        WallpaperManager wallpaperManager = null;
-        if (mSetWallpaper) {
-            wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
-        }
-
-
         if (mSetWallpaper && mNoCrop) {
             try {
                 InputStream is = regenerateInputStream();
-                if (is != null) {
-                    if (!Utilities.isNycOrAbove()) {
-                        wallpaperManager.setStream(is);
-                    } else {
-                        NycWallpaperUtils.setStream(mContext, is, null, true, whichWallpaper);
-                    }
-                    Utils.closeSilently(is);
-                }
+                setWallpaper(is, null, whichWallpaper);
+                Utils.closeSilently(is);
             } catch (IOException e) {
                 Log.w(LOGTAG, "cannot write stream to wallpaper", e);
                 failure = true;
             }
             return !failure;
+        } else if (mSetWallpaper && Utilities.isNycOrAbove()
+                && mRotation == 0 && mOutWidth > 0 && mOutHeight > 0) {
+            Rect hint = new Rect();
+            mCropBounds.roundOut(hint);
+
+            InputStream is = null;
+            try {
+                is = regenerateInputStream();
+                if (is == null) {
+                    Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
+                    failure = true;
+                    return false;
+                }
+                WallpaperManager.getInstance(mContext).suggestDesiredDimensions(mOutWidth, mOutHeight);
+                setWallpaper(is, hint, whichWallpaper);
+
+                if (mOnBitmapCroppedHandler != null) {
+                    mOnBitmapCroppedHandler.onBitmapCropped(null, hint);
+                }
+
+                failure = false;
+            } catch (IOException e) {
+                Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
+            } finally {
+                Utils.closeSilently(is);
+            }
         } else {
             // Find crop bounds (scaled to original image size)
             Rect roundedTrueCrop = new Rect();
@@ -222,7 +235,6 @@
                 mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2);
                 inverseRotateMatrix.mapRect(mCropBounds);
                 mCropBounds.offset(bounds.x/2, bounds.y/2);
-
             }
 
             mCropBounds.roundOut(roundedTrueCrop);
@@ -369,18 +381,13 @@
             ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
             if (crop.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
                 // If we need to set to the wallpaper, set it
-                if (mSetWallpaper && wallpaperManager != null) {
+                if (mSetWallpaper) {
                     try {
                         byte[] outByteArray = tmpOut.toByteArray();
-                        if (!Utilities.isNycOrAbove()) {
-                            wallpaperManager.setStream(new ByteArrayInputStream(outByteArray));
-                        } else {
-                            NycWallpaperUtils.setStream(mContext,
-                                    new ByteArrayInputStream(outByteArray), null, true,
-                                    whichWallpaper);
-                        }
+                        setWallpaper(new ByteArrayInputStream(outByteArray), null, whichWallpaper);
                         if (mOnBitmapCroppedHandler != null) {
-                            mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
+                            mOnBitmapCroppedHandler.onBitmapCropped(outByteArray,
+                                    new Rect(0, 0, crop.getWidth(), crop.getHeight()));
                         }
                     } catch (IOException e) {
                         Log.w(LOGTAG, "cannot write stream to wallpaper", e);
@@ -409,4 +416,12 @@
             mOnEndCropHandler.run(cropSucceeded);
         }
     }
+
+    private void setWallpaper(InputStream in, Rect crop, int whichWallpaper) throws IOException {
+        if (!Utilities.isNycOrAbove()) {
+            WallpaperManager.getInstance(mContext.getApplicationContext()).setStream(in);
+        } else {
+            NycWallpaperUtils.setStream(mContext, in, crop, true, whichWallpaper);
+        }
+    }
 }
\ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/launcher3/CropView.java b/WallpaperPicker/src/com/android/launcher3/CropView.java
index e98e23e..c1578c1 100644
--- a/WallpaperPicker/src/com/android/launcher3/CropView.java
+++ b/WallpaperPicker/src/com/android/launcher3/CropView.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.RectF;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
@@ -148,12 +149,19 @@
         updateMinScale(w, h, mRenderer.source, false);
     }
 
-    public void setScale(float scale) {
+    public void setScaleAndCenter(float scale, float x, float y) {
         synchronized (mLock) {
             mRenderer.scale = scale;
+            mCenterX = x;
+            mCenterY = y;
+            updateCenter();
         }
     }
 
+    public float getScale() {
+        return mRenderer.scale;
+    }
+
     private void updateMinScale(int w, int h, TileSource source, boolean resetScale) {
         synchronized (mLock) {
             if (resetScale) {
@@ -189,17 +197,6 @@
     public void onScaleEnd(ScaleGestureDetector detector) {
     }
 
-    /**
-     * Offsets wallpaper preview according to the state it will be displayed in upon returning home.
-     * @param offset Ranges from 0 to 1, where 0 is the leftmost parallax and 1 is the rightmost.
-     */
-    public void setParallaxOffset(float offset, RectF crop) {
-        offset = Math.max(0, Math.min(offset, 1)); // Make sure the offset is in the correct range.
-        float screenWidth = getWidth() / mRenderer.scale;
-        mCenterX = screenWidth / 2 + offset * (crop.width() - screenWidth) + crop.left;
-        updateCenter();
-    }
-
     public void moveToLeft() {
         if (getWidth() == 0 || getHeight() == 0) {
             final ViewTreeObserver observer = getViewTreeObserver();
@@ -222,6 +219,10 @@
         mRenderer.centerY = Math.round(mCenterY);
     }
 
+    public PointF getCenter() {
+        return new PointF(mCenterX, mCenterY);
+    }
+
     public void setTouchEnabled(boolean enabled) {
         mTouchEnabled = enabled;
     }
diff --git a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
index 64b0ac4..34226f7 100644
--- a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
+++ b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
@@ -25,6 +25,8 @@
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 import android.view.LayoutInflater;
@@ -33,9 +35,14 @@
 import android.widget.BaseAdapter;
 import android.widget.ListAdapter;
 
+import com.android.launcher3.WallpaperCropActivity.CropViewScaleAndOffsetProvider;
+import com.android.photos.views.TiledImageRenderer.TileSource;
+
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 
 
@@ -48,15 +55,42 @@
 
     public static class SavedWallpaperTile extends WallpaperPickerActivity.FileWallpaperInfo {
         private int mDbId;
-        public SavedWallpaperTile(int dbId, File target, Drawable thumb) {
+
+        // three floats representing scale, centerX and centerY of the crop view in order.
+        private Float[] mExtras;
+        public SavedWallpaperTile(int dbId, File target, Drawable thumb, Float[] extras) {
             super(target, thumb);
             mDbId = dbId;
+            mExtras = extras != null && extras.length == 3 ? extras : null;
         }
 
         @Override
         public void onDelete(WallpaperPickerActivity a) {
             a.getSavedImages().deleteImage(mDbId);
         }
+
+        @Override
+        protected CropViewScaleAndOffsetProvider getCropViewScaleAndOffsetProvider() {
+            if (mExtras != null) {
+                return new CropViewScaleAndOffsetProvider() {
+                    @Override
+                    public void updateCropView(WallpaperCropActivity a, TileSource src) {
+                        a.mCropView.setScaleAndCenter(mExtras[0], mExtras[1], mExtras[2]);
+                    }
+                };
+            }
+            return null;
+        }
+
+        @Override
+        public void onSave(final WallpaperPickerActivity a) {
+            if (mExtras == null) {
+                super.onSave(a);
+            } else {
+                boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f;
+                a.cropImageAndSetWallpaper(Uri.fromFile(mFile), null, true, shouldFadeOutOnFinish);
+            }
+        }
     }
 
     public SavedWallpaperImages(Context context) {
@@ -74,7 +108,8 @@
         Cursor result = db.query(ImageDb.TABLE_NAME,
                 new String[] { ImageDb.COLUMN_ID,
                     ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME,
-                    ImageDb.COLUMN_IMAGE_FILENAME}, // cols to return
+                    ImageDb.COLUMN_IMAGE_FILENAME,
+                    ImageDb.COLUMN_EXTRAS}, // cols to return
                 null, // select query
                 null, // args to select query
                 null,
@@ -88,9 +123,25 @@
 
             Bitmap thumb = BitmapFactory.decodeFile(file.getAbsolutePath());
             if (thumb != null) {
+
+                Float[] extras = null;
+                String extraStr = result.getString(3);
+                if (extraStr != null) {
+                    String[] parts = extraStr.split(",");
+                    extras = new Float[parts.length];
+                    for (int i = 0; i < parts.length; i++) {
+                        try {
+                            extras[i] = Float.parseFloat(parts[i]);
+                        } catch (Exception e) {
+                            extras = null;
+                            break;
+                        }
+                    }
+                }
+
                 mImages.add(new SavedWallpaperTile(result.getInt(0),
                         new File(mContext.getFilesDir(), result.getString(2)),
-                        new BitmapDrawable(thumb)));
+                        new BitmapDrawable(thumb), extras));
             }
         }
         result.close();
@@ -155,34 +206,55 @@
 
     public void writeImage(Bitmap thumbnail, byte[] imageBytes) {
         try {
-            File imageFile = File.createTempFile("wallpaper", "", mContext.getFilesDir());
-            FileOutputStream imageFileStream =
-                    mContext.openFileOutput(imageFile.getName(), Context.MODE_PRIVATE);
-            imageFileStream.write(imageBytes);
-            imageFileStream.close();
-
-            File thumbFile = File.createTempFile("wallpaperthumb", "", mContext.getFilesDir());
-            FileOutputStream thumbFileStream =
-                    mContext.openFileOutput(thumbFile.getName(), Context.MODE_PRIVATE);
-            thumbnail.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream);
-            thumbFileStream.close();
-
-            SQLiteDatabase db = mDb.getWritableDatabase();
-            ContentValues values = new ContentValues();
-            values.put(ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME, thumbFile.getName());
-            values.put(ImageDb.COLUMN_IMAGE_FILENAME, imageFile.getName());
-            db.insert(ImageDb.TABLE_NAME, null, values);
+            writeImage(thumbnail, new ByteArrayInputStream(imageBytes), null);
         } catch (IOException e) {
             Log.e(TAG, "Failed writing images to storage " + e);
         }
     }
 
+    public void writeImage(Bitmap thumbnail, Uri uri, Float[] extras) {
+        try {
+            writeImage(thumbnail, mContext.getContentResolver().openInputStream(uri), extras);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed writing images to storage " + e);
+        }
+    }
+
+    private void writeImage(Bitmap thumbnail, InputStream in, Float[] extras) throws IOException {
+        File imageFile = File.createTempFile("wallpaper", "", mContext.getFilesDir());
+        FileOutputStream imageFileStream =
+                mContext.openFileOutput(imageFile.getName(), Context.MODE_PRIVATE);
+        byte[] buf = new byte[4096];    // 4k
+        int len;
+        while ((len = in.read(buf)) > 0) {
+            imageFileStream.write(buf, 0, len);
+        }
+        imageFileStream.close();
+        in.close();
+
+        File thumbFile = File.createTempFile("wallpaperthumb", "", mContext.getFilesDir());
+        FileOutputStream thumbFileStream =
+                mContext.openFileOutput(thumbFile.getName(), Context.MODE_PRIVATE);
+        thumbnail.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream);
+        thumbFileStream.close();
+
+        SQLiteDatabase db = mDb.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME, thumbFile.getName());
+        values.put(ImageDb.COLUMN_IMAGE_FILENAME, imageFile.getName());
+        if (extras != null) {
+            values.put(ImageDb.COLUMN_EXTRAS, TextUtils.join(",", extras));
+        }
+        db.insert(ImageDb.TABLE_NAME, null, values);
+    }
+
     static class ImageDb extends SQLiteOpenHelper {
-        final static int DB_VERSION = 1;
+        final static int DB_VERSION = 2;
         final static String TABLE_NAME = "saved_wallpaper_images";
         final static String COLUMN_ID = "id";
         final static String COLUMN_IMAGE_THUMBNAIL_FILENAME = "image_thumbnail";
         final static String COLUMN_IMAGE_FILENAME = "image";
+        final static String COLUMN_EXTRAS = "extras";
 
         Context mContext;
 
@@ -209,15 +281,20 @@
                     COLUMN_ID + " INTEGER NOT NULL, " +
                     COLUMN_IMAGE_THUMBNAIL_FILENAME + " TEXT NOT NULL, " +
                     COLUMN_IMAGE_FILENAME + " TEXT NOT NULL, " +
+                    COLUMN_EXTRAS + " TEXT, " +
                     "PRIMARY KEY (" + COLUMN_ID + " ASC) " +
                     ");");
         }
 
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            if (oldVersion != newVersion) {
+            if (oldVersion == 1) {
+                // Add extras column
+                db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + COLUMN_EXTRAS + " TEXT;");
+            } else if (oldVersion != newVersion) {
                 // Delete all the records; they'll be repopulated as this is a cache
                 db.execSQL("DELETE FROM " + TABLE_NAME);
+                onCreate(db);
             }
         }
     }
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
index 2f21145..f6c53ec 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
@@ -29,6 +29,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Matrix;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.RectF;
 import android.net.Uri;
 import android.os.Build;
@@ -269,13 +270,7 @@
                 mCropView.moveToLeft();
             }
             if (req.scaleAndOffsetProvider != null) {
-                TileSource src = req.result;
-                Point wallpaperSize = WallpaperUtils.getDefaultWallpaperSize(
-                        getResources(), getWindowManager());
-                RectF crop = Utils.getMaxCropRect(src.getImageWidth(), src.getImageHeight(),
-                        wallpaperSize.x, wallpaperSize.y, false /* leftAligned */);
-                mCropView.setScale(req.scaleAndOffsetProvider.getScale(wallpaperSize, crop));
-                mCropView.setParallaxOffset(req.scaleAndOffsetProvider.getParallaxOffset(), crop);
+                req.scaleAndOffsetProvider.updateCropView(this, req.result);
             }
 
             // Free last image
@@ -502,8 +497,32 @@
         TileSource result;
     }
 
-    interface CropViewScaleAndOffsetProvider {
-        float getScale(Point wallpaperSize, RectF crop);
-        float getParallaxOffset();
+    public static class CropViewScaleAndOffsetProvider {
+        public float getScale(Point wallpaperSize, RectF crop) {
+            return 1f;
+        }
+
+        public float getParallaxOffset() {
+            return 0.5f;
+        }
+
+        public void updateCropView(WallpaperCropActivity a, TileSource src) {
+            Point wallpaperSize = WallpaperUtils.getDefaultWallpaperSize(
+                    a.getResources(), a.getWindowManager());
+            RectF crop = Utils.getMaxCropRect(src.getImageWidth(), src.getImageHeight(),
+                    wallpaperSize.x, wallpaperSize.y, false /* leftAligned */);
+
+            float scale = getScale(wallpaperSize, crop);
+            PointF center = a.mCropView.getCenter();
+
+            // Offsets wallpaper preview according to the state it will be displayed in upon
+            // returning home. Offset ranges from 0 to 1, where 0 is the leftmost parallax and
+            // 1 is the rightmost.
+            // Make sure the offset is in the correct range.
+            float offset = Math.max(0, Math.min(getParallaxOffset(), 1));
+            float screenWidth = a.mCropView.getWidth() / scale;
+            center.x = screenWidth / 2 + offset * (crop.width() - screenWidth) + crop.left;
+            a.mCropView.setScaleAndCenter(scale, center.x, center.y);
+        }
     }
 }
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index 5c6113f..40f0544 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -31,10 +31,13 @@
 import android.database.DataSetObserver;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Point;
+import android.graphics.PointF;
 import android.graphics.PorterDuff;
+import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -83,6 +86,7 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.ArrayList;
 
 public class WallpaperPickerActivity extends WallpaperCropActivity {
@@ -174,12 +178,45 @@
         public void onSave(final WallpaperPickerActivity a) {
             boolean finishActivityWhenDone = true;
             BitmapCropTask.OnBitmapCroppedHandler h = new BitmapCropTask.OnBitmapCroppedHandler() {
-                public void onBitmapCropped(byte[] imageBytes) {
+                @Override
+                public void onBitmapCropped(byte[] imageBytes, Rect hint) {
+                    Bitmap thumb = null;
                     Point thumbSize = getDefaultThumbnailSize(a.getResources());
-                    // rotation is set to 0 since imageBytes has already been correctly rotated
-                    Bitmap thumb = createThumbnail(
-                            thumbSize, null, null, imageBytes, null, 0, 0, true);
-                    a.getSavedImages().writeImage(thumb, imageBytes);
+                    if (imageBytes != null) {
+                        // rotation is set to 0 since imageBytes has already been correctly rotated
+                        thumb = createThumbnail(
+                                thumbSize, null, null, imageBytes, null, 0, 0, true);
+                        a.getSavedImages().writeImage(thumb, imageBytes);
+                    } else {
+                        try {
+                            // Generate thumb
+                            Point size = getDefaultThumbnailSize(a.getResources());
+                            Rect finalCropped = new Rect();
+                            Utils.getMaxCropRect(hint.width(), hint.height(), size.x, size.y, false)
+                                    .roundOut(finalCropped);
+                            finalCropped.offset(hint.left, hint.top);
+
+                            InputStream in = a.getContentResolver().openInputStream(mUri);
+                            BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(in, true);
+
+                            BitmapFactory.Options options = new BitmapFactory.Options();
+                            options.inSampleSize = finalCropped.width() / size.x;
+                            thumb = decoder.decodeRegion(finalCropped, options);
+                            decoder.recycle();
+                            Utils.closeSilently(in);
+                            if (thumb != null) {
+                                thumb = Bitmap.createScaledBitmap(thumb, size.x, size.y, true);
+                            }
+                        } catch (IOException e) { }
+                        PointF center = a.mCropView.getCenter();
+
+                        Float[] extras = new Float[] {
+                                a.mCropView.getScale(),
+                                center.x,
+                                center.y
+                        };
+                        a.getSavedImages().writeImage(thumb, mUri, extras);
+                    }
                 }
             };
             boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f;
@@ -196,7 +233,7 @@
     }
 
     public static class FileWallpaperInfo extends WallpaperTileInfo {
-        private File mFile;
+        protected File mFile;
 
         public FileWallpaperInfo(File target, Drawable thumb) {
             mFile = target;
@@ -207,7 +244,8 @@
             a.setWallpaperButtonEnabled(false);
             final BitmapRegionTileSource.UriBitmapSource bitmapSource =
                     new BitmapRegionTileSource.UriBitmapSource(a.getContext(), Uri.fromFile(mFile));
-            a.setCropViewTileSource(bitmapSource, false, true, null, new Runnable() {
+            a.setCropViewTileSource(bitmapSource, false, true, getCropViewScaleAndOffsetProvider(),
+                    new Runnable() {
 
                 @Override
                 public void run() {
@@ -217,6 +255,11 @@
                 }
             });
         }
+
+        protected CropViewScaleAndOffsetProvider getCropViewScaleAndOffsetProvider() {
+            return null;
+        }
+
         @Override
         public void onSave(WallpaperPickerActivity a) {
             boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f;
@@ -303,18 +346,7 @@
             LoadRequest req = new LoadRequest();
             req.moveToLeft = false;
             req.touchEnabled = false;
-            req.scaleAndOffsetProvider = new CropViewScaleAndOffsetProvider() {
-
-                @Override
-                public float getScale(Point wallpaperSize, RectF crop) {
-                    return 1f;
-                }
-
-                @Override
-                public float getParallaxOffset() {
-                    return 0.5f;
-                }
-            };
+            req.scaleAndOffsetProvider = new CropViewScaleAndOffsetProvider();
             req.result = new DrawableTileSource(a.getContext(),
                     defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE);
             a.onLoadRequestComplete(req, true);