From fceb9ff0f5855432763d5dc15eeff8e7ce4df0c0 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 15 Apr 2016 17:45:39 -0700 Subject: [PATCH] Passing the full image and crop hint when setting the wallpaper Bug: 25454157 Change-Id: Ia0ae7dd4963b72cd7902622847deedfcb5a0eca2 --- .../gallery3d/common/BitmapCropTask.java | 67 ++++++---- .../src/com/android/launcher3/CropView.java | 25 ++-- .../launcher3/SavedWallpaperImages.java | 121 ++++++++++++++---- .../launcher3/WallpaperCropActivity.java | 39 ++++-- .../launcher3/WallpaperPickerActivity.java | 70 +++++++--- 5 files changed, 233 insertions(+), 89 deletions(-) diff --git a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java b/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java index 027e94708e..153fa90587 100644 --- a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java +++ b/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java @@ -47,7 +47,7 @@ import java.io.InputStream; public class BitmapCropTask extends AsyncTask { public interface OnBitmapCroppedHandler { - public void onBitmapCropped(byte[] imageBytes); + public void onBitmapCropped(byte[] imageBytes, Rect cropHint); } public interface OnEndCropHandler { @@ -171,29 +171,42 @@ public class BitmapCropTask extends AsyncTask { 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 @@ public class BitmapCropTask extends AsyncTask { 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 @@ public class BitmapCropTask extends AsyncTask { 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 @@ public class BitmapCropTask extends AsyncTask { 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 e98e23e981..c1578c150a 100644 --- a/WallpaperPicker/src/com/android/launcher3/CropView.java +++ b/WallpaperPicker/src/com/android/launcher3/CropView.java @@ -19,6 +19,7 @@ package com.android.launcher3; 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 @@ public class CropView extends TiledImageView implements OnScaleGestureListener 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 class CropView extends TiledImageView implements OnScaleGestureListener 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 @@ public class CropView extends TiledImageView implements OnScaleGestureListener 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 64b0ac4662..34226f7ae2 100644 --- a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java +++ b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java @@ -25,6 +25,8 @@ import android.graphics.Bitmap; 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.view.ViewGroup; 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 class SavedWallpaperImages extends BaseAdapter implements ListAdapter { 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 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter { 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 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter { 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 class SavedWallpaperImages extends BaseAdapter implements ListAdapter { 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 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter { 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 2f21145637..f6c53ec374 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java @@ -29,6 +29,7 @@ import android.content.res.Resources; 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 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb 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 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb 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 5c6113f95a..40f0544a2d 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -31,10 +31,13 @@ import android.database.Cursor; 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.ByteArrayOutputStream; 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 class WallpaperPickerActivity extends WallpaperCropActivity { 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 class WallpaperPickerActivity extends WallpaperCropActivity { } public static class FileWallpaperInfo extends WallpaperTileInfo { - private File mFile; + protected File mFile; public FileWallpaperInfo(File target, Drawable thumb) { mFile = target; @@ -207,7 +244,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { 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 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { } }); } + + protected CropViewScaleAndOffsetProvider getCropViewScaleAndOffsetProvider() { + return null; + } + @Override public void onSave(WallpaperPickerActivity a) { boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f; @@ -303,18 +346,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { 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);