| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.launcher3; |
| |
| import android.app.Activity; |
| import android.app.WallpaperManager; |
| import android.content.Context; |
| import android.content.SharedPreferences; |
| import android.content.res.Resources; |
| import android.graphics.Bitmap; |
| import android.graphics.Bitmap.CompressFormat; |
| import android.graphics.BitmapFactory; |
| import android.graphics.BitmapRegionDecoder; |
| import android.graphics.Canvas; |
| import android.graphics.Matrix; |
| import android.graphics.Paint; |
| import android.graphics.Point; |
| import android.graphics.Rect; |
| import android.graphics.RectF; |
| import android.net.Uri; |
| import android.os.AsyncTask; |
| import android.util.Log; |
| |
| import com.android.gallery3d.common.Utils; |
| |
| import java.io.BufferedInputStream; |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| |
| // LAUNCHER crop activity! |
| public class WallpaperCropActivity extends Activity { |
| private static final String LOGTAG = "Launcher3.CropActivity"; |
| |
| protected static final String WALLPAPER_WIDTH_KEY = "wallpaper.width"; |
| protected static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height"; |
| private static final int DEFAULT_COMPRESS_QUALITY = 90; |
| /** |
| * The maximum bitmap size we allow to be returned through the intent. |
| * Intents have a maximum of 1MB in total size. However, the Bitmap seems to |
| * have some overhead to hit so that we go way below the limit here to make |
| * sure the intent stays below 1MB.We should consider just returning a byte |
| * array instead of a Bitmap instance to avoid overhead. |
| */ |
| public static final int MAX_BMAP_IN_INTENT = 750000; |
| |
| |
| protected class BitmapCropTask extends AsyncTask<Void, Void, Boolean> { |
| Uri mInUri = null; |
| int mInResId = 0; |
| InputStream mInStream; |
| RectF mCropBounds = null; |
| int mOutWidth, mOutHeight; |
| int mRotation = 0; // for now |
| protected final WallpaperManager mWPManager; |
| String mOutputFormat = "jpg"; // for now |
| boolean mSetWallpaper; |
| boolean mSaveCroppedBitmap; |
| Bitmap mCroppedBitmap; |
| Runnable mOnEndRunnable; |
| Resources mResources; |
| |
| public BitmapCropTask(Uri inUri, |
| RectF cropBounds, int outWidth, int outHeight, |
| boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { |
| mInUri = inUri; |
| mCropBounds = cropBounds; |
| mOutWidth = outWidth; |
| mOutHeight = outHeight; |
| mWPManager = WallpaperManager.getInstance(getApplicationContext()); |
| mSetWallpaper = setWallpaper; |
| mSaveCroppedBitmap = saveCroppedBitmap; |
| mOnEndRunnable = onEndRunnable; |
| } |
| |
| public BitmapCropTask(Resources res, int inResId, |
| RectF cropBounds, int outWidth, int outHeight, |
| boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { |
| mInResId = inResId; |
| mCropBounds = cropBounds; |
| mOutWidth = outWidth; |
| mOutHeight = outHeight; |
| mWPManager = WallpaperManager.getInstance(getApplicationContext()); |
| mSetWallpaper = setWallpaper; |
| mSaveCroppedBitmap = saveCroppedBitmap; |
| mOnEndRunnable = onEndRunnable; |
| mResources = res; |
| } |
| |
| // Helper to setup input stream |
| private void regenerateInputStream() { |
| if (mInUri == null && mInResId == 0) { |
| Log.w(LOGTAG, "cannot read original file, no input URI or resource ID given"); |
| } else { |
| Utils.closeSilently(mInStream); |
| try { |
| if (mInUri != null) { |
| mInStream = new BufferedInputStream( |
| getContentResolver().openInputStream(mInUri)); |
| } else { |
| mInStream = new BufferedInputStream( |
| mResources.openRawResource(mInResId)); |
| } |
| } catch (FileNotFoundException e) { |
| Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e); |
| } |
| } |
| } |
| |
| public Point getImageBounds() { |
| regenerateInputStream(); |
| if (mInStream != null) { |
| BitmapFactory.Options options = new BitmapFactory.Options(); |
| options.inJustDecodeBounds = true; |
| BitmapFactory.decodeStream(mInStream, null, options); |
| if (options.outWidth != 0 && options.outHeight != 0) { |
| return new Point(options.outWidth, options.outHeight); |
| } |
| } |
| return null; |
| } |
| |
| public void setCropBounds(RectF cropBounds) { |
| mCropBounds = cropBounds; |
| } |
| |
| public Bitmap getCroppedBitmap() { |
| return mCroppedBitmap; |
| } |
| public boolean cropBitmap() { |
| boolean failure = false; |
| |
| regenerateInputStream(); |
| |
| if (mInStream != null) { |
| // Find crop bounds (scaled to original image size) |
| Rect roundedTrueCrop = new Rect(); |
| mCropBounds.roundOut(roundedTrueCrop); |
| |
| if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { |
| Log.w(LOGTAG, "crop has bad values for full size image"); |
| failure = true; |
| return false; |
| } |
| |
| // See how much we're reducing the size of the image |
| int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / mOutWidth, |
| roundedTrueCrop.height() / mOutHeight); |
| |
| // Attempt to open a region decoder |
| BitmapRegionDecoder decoder = null; |
| try { |
| decoder = BitmapRegionDecoder.newInstance(mInStream, true); |
| } catch (IOException e) { |
| Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e); |
| } |
| |
| Bitmap crop = null; |
| if (decoder != null) { |
| // Do region decoding to get crop bitmap |
| BitmapFactory.Options options = new BitmapFactory.Options(); |
| if (scaleDownSampleSize > 1) { |
| options.inSampleSize = scaleDownSampleSize; |
| } |
| crop = decoder.decodeRegion(roundedTrueCrop, options); |
| decoder.recycle(); |
| } |
| |
| if (crop == null) { |
| // BitmapRegionDecoder has failed, try to crop in-memory |
| regenerateInputStream(); |
| Bitmap fullSize = null; |
| if (mInStream != null) { |
| BitmapFactory.Options options = new BitmapFactory.Options(); |
| if (scaleDownSampleSize > 1) { |
| options.inSampleSize = scaleDownSampleSize; |
| } |
| fullSize = BitmapFactory.decodeStream(mInStream, null, options); |
| } |
| if (fullSize != null) { |
| crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, |
| roundedTrueCrop.top, roundedTrueCrop.width(), |
| roundedTrueCrop.height()); |
| } |
| } |
| |
| if (crop == null) { |
| Log.w(LOGTAG, "cannot decode file: " + mInUri.toString()); |
| failure = true; |
| return false; |
| } |
| if (mOutWidth > 0 && mOutHeight > 0) { |
| Matrix m = new Matrix(); |
| RectF cropRect = new RectF(0, 0, crop.getWidth(), crop.getHeight()); |
| if (mRotation > 0) { |
| m.setRotate(mRotation); |
| m.mapRect(cropRect); |
| } |
| RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight); |
| m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); |
| m.preRotate(mRotation); |
| Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), |
| (int) returnRect.height(), Bitmap.Config.ARGB_8888); |
| if (tmp != null) { |
| Canvas c = new Canvas(tmp); |
| c.drawBitmap(crop, m, new Paint()); |
| crop = tmp; |
| } |
| } else if (mRotation > 0) { |
| Matrix m = new Matrix(); |
| m.setRotate(mRotation); |
| Bitmap tmp = Bitmap.createBitmap(crop, 0, 0, crop.getWidth(), |
| crop.getHeight(), m, true); |
| if (tmp != null) { |
| crop = tmp; |
| } |
| } |
| |
| if (mSaveCroppedBitmap) { |
| mCroppedBitmap = crop; |
| } |
| |
| // Get output compression format |
| CompressFormat cf = |
| convertExtensionToCompressFormat(getFileExtension(mOutputFormat)); |
| |
| // Compress to byte array |
| ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048); |
| if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) { |
| // If we need to set to the wallpaper, set it |
| if (mSetWallpaper && mWPManager != null) { |
| if (mWPManager == null) { |
| Log.w(LOGTAG, "no wallpaper manager"); |
| failure = true; |
| } else { |
| try { |
| mWPManager.setStream(new ByteArrayInputStream(tmpOut |
| .toByteArray())); |
| } catch (IOException e) { |
| Log.w(LOGTAG, "cannot write stream to wallpaper", e); |
| failure = true; |
| } |
| } |
| } |
| if (mOnEndRunnable != null) { |
| mOnEndRunnable.run(); |
| } |
| } else { |
| Log.w(LOGTAG, "cannot compress bitmap"); |
| failure = true; |
| } |
| } |
| return !failure; // True if any of the operations failed |
| } |
| |
| @Override |
| protected Boolean doInBackground(Void... params) { |
| return cropBitmap(); |
| } |
| |
| @Override |
| protected void onPostExecute(Boolean result) { |
| setResult(Activity.RESULT_OK); |
| finish(); |
| } |
| } |
| |
| protected void updateWallpaperDimensions(int width, int height) { |
| String spKey = LauncherAppState.getSharedPreferencesKey(); |
| SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE); |
| SharedPreferences.Editor editor = sp.edit(); |
| if (width != 0 && height != 0) { |
| editor.putInt(WALLPAPER_WIDTH_KEY, width); |
| editor.putInt(WALLPAPER_HEIGHT_KEY, height); |
| } else { |
| editor.remove(WALLPAPER_WIDTH_KEY); |
| editor.remove(WALLPAPER_HEIGHT_KEY); |
| } |
| editor.commit(); |
| |
| WallpaperPickerActivity.suggestWallpaperDimension(getResources(), |
| sp, getWindowManager(), WallpaperManager.getInstance(this)); |
| } |
| |
| protected static CompressFormat convertExtensionToCompressFormat(String extension) { |
| return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG; |
| } |
| |
| protected static String getFileExtension(String requestFormat) { |
| String outputFormat = (requestFormat == null) |
| ? "jpg" |
| : requestFormat; |
| outputFormat = outputFormat.toLowerCase(); |
| return (outputFormat.equals("png") || outputFormat.equals("gif")) |
| ? "png" // We don't support gif compression. |
| : "jpg"; |
| } |
| } |