WallpaperCropActivity.java revision 7b215cb92288aa0a21bc773511ddd537b8fbb459
1e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka/* 2e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Copyright (C) 2013 The Android Open Source Project 3e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 4e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License"); 5e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * you may not use this file except in compliance with the License. 6e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * You may obtain a copy of the License at 7e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 8e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * http://www.apache.org/licenses/LICENSE-2.0 9e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * 10e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Unless required by applicable law or agreed to in writing, software 11e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS, 12e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * See the License for the specific language governing permissions and 14e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * limitations under the License. 15e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 16e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka/* Copied from Launcher3 */ 17e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapackage com.android.wallpapercropper; 18e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 19e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.app.ActionBar; 20e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.app.Activity; 21e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.app.WallpaperManager; 22e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.Context; 23e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.Intent; 24e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.SharedPreferences; 25e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.res.Configuration; 26e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.res.Resources; 27e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Bitmap; 28e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Bitmap.CompressFormat; 29e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.BitmapFactory; 30e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.BitmapRegionDecoder; 31e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Canvas; 32e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Matrix; 33e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Paint; 34e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Point; 35e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Rect; 36e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.RectF; 37e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.net.Uri; 38e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.os.AsyncTask; 39e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.os.Bundle; 40e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.Log; 41e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.Display; 42e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.View; 43e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.WindowManager; 447b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurkaimport android.widget.Toast; 45e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 46e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.gallery3d.common.Utils; 4769784065ea78550944b7eddb559c0dac952e20e8Michael Jurkaimport com.android.gallery3d.exif.ExifInterface; 48e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.photos.BitmapRegionTileSource; 497b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurkaimport com.android.photos.BitmapRegionTileSource.BitmapSource; 50e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 51e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.BufferedInputStream; 52e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.ByteArrayInputStream; 53e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.ByteArrayOutputStream; 54e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.FileNotFoundException; 55e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.IOException; 56e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.InputStream; 57e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 58e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapublic class WallpaperCropActivity extends Activity { 59e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final String LOGTAG = "Launcher3.CropActivity"; 60e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 61e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected static final String WALLPAPER_WIDTH_KEY = "wallpaper.width"; 62e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height"; 63e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int DEFAULT_COMPRESS_QUALITY = 90; 64e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka /** 65e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * The maximum bitmap size we allow to be returned through the intent. 66e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Intents have a maximum of 1MB in total size. However, the Bitmap seems to 67e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * have some overhead to hit so that we go way below the limit here to make 68e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * sure the intent stays below 1MB.We should consider just returning a byte 69e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * array instead of a Bitmap instance to avoid overhead. 70e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 71e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public static final int MAX_BMAP_IN_INTENT = 750000; 72e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final float WALLPAPER_SCREENS_SPAN = 2f; 73e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 74e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected CropView mCropView; 75e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected Uri mUri; 76e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 77e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 78e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void onCreate(Bundle savedInstanceState) { 79e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka super.onCreate(savedInstanceState); 80e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka init(); 81e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka if (!enableRotation()) { 82e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT); 83e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } 84e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 85e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 86e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void init() { 87e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka setContentView(R.layout.wallpaper_cropper); 88e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 89e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mCropView = (CropView) findViewById(R.id.cropView); 90e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 9169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Intent cropIntent = getIntent(); 92e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final Uri imageUri = cropIntent.getData(); 93e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 9469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (imageUri == null) { 9569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Log.e(LOGTAG, "No URI passed in intent, exiting WallpaperCropActivity"); 9669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka finish(); 9769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return; 9869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 9969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 100e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Action bar 101e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Show the custom action bar view 102e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final ActionBar actionBar = getActionBar(); 103e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka actionBar.setCustomView(R.layout.actionbar_set_wallpaper); 104e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka actionBar.getCustomView().setOnClickListener( 105e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka new View.OnClickListener() { 106e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 107e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void onClick(View v) { 108e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean finishActivityWhenDone = true; 109e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone); 110e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 111e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka }); 1125271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka 1135271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka // Load image in background 1147b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka final BitmapRegionTileSource.UriBitmapSource bitmapSource = 1157b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024); 1167b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Runnable onLoad = new Runnable() { 1177b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka public void run() { 1187b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) { 1197b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Toast.makeText(WallpaperCropActivity.this, 1207b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka getString(R.string.wallpaper_load_fail), 1217b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Toast.LENGTH_LONG).show(); 1227b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka finish(); 1237b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } 1247b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } 1257b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka }; 1267b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka setCropViewTileSource(bitmapSource, true, false, onLoad); 1275271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1285271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka 1297b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka public void setCropViewTileSource( 1307b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka final BitmapRegionTileSource.BitmapSource bitmapSource, final boolean touchEnabled, 1317b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka final boolean moveToLeft, final Runnable postExecute) { 1325271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka final Context context = WallpaperCropActivity.this; 1335271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka final View progressView = findViewById(R.id.loading); 1345271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() { 1355271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka protected Void doInBackground(Void...args) { 1365271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka if (!isCancelled()) { 1375271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka bitmapSource.loadInBackground(); 1385271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1395271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka return null; 1405271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1415271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka protected void onPostExecute(Void arg) { 1425271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka if (!isCancelled()) { 1435271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka progressView.setVisibility(View.INVISIBLE); 1447b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) { 1457b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka mCropView.setTileSource( 1467b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka new BitmapRegionTileSource(context, bitmapSource), null); 1477b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka mCropView.setTouchEnabled(touchEnabled); 1487b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (moveToLeft) { 1497b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka mCropView.moveToLeft(); 1507b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } 1515271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1525271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1537b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (postExecute != null) { 1547b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka postExecute.run(); 1557b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } 1565271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1575271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka }; 1585271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka // We don't want to show the spinner every time we load an image, because that would be 1595271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka // annoying; instead, only start showing the spinner if loading the image has taken 1605271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka // longer than 1 sec (ie 1000 ms) 1615271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka progressView.postDelayed(new Runnable() { 1625271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka public void run() { 1635271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka if (loadBitmapTask.getStatus() != AsyncTask.Status.FINISHED) { 1645271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka progressView.setVisibility(View.VISIBLE); 1655271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1665271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1675271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka }, 1000); 1685271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka loadBitmapTask.execute(); 169e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } 170e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka 171e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka public boolean enableRotation() { 172e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka return getResources().getBoolean(R.bool.allow_rotation); 173e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 174e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 175e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public static String getSharedPreferencesKey() { 176e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return WallpaperCropActivity.class.getName(); 177e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 178e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 179e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // As a ratio of screen height, the total distance we want the parallax effect to span 180e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // horizontally 181e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static float wallpaperTravelToScreenWidthRatio(int width, int height) { 182e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float aspectRatio = width / (float) height; 183e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 184e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width 185e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width 186e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // We will use these two data points to extrapolate how much the wallpaper parallax effect 187e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // to span (ie travel) at any aspect ratio: 188e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 189e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float ASPECT_RATIO_LANDSCAPE = 16/10f; 190e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float ASPECT_RATIO_PORTRAIT = 10/16f; 191e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f; 192e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f; 193e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 194e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // To find out the desired width at different aspect ratios, we use the following two 195e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // formulas, where the coefficient on x is the aspect ratio (width/height): 196e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // (16/10)x + y = 1.5 197e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // (10/16)x + y = 1.2 198e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // We solve for x and y and end up with a final formula: 199e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float x = 200e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) / 201e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT); 202e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT; 203e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return x * aspectRatio + y; 204e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 205e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 206e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka static protected Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) { 207e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Point minDims = new Point(); 208e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Point maxDims = new Point(); 209e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims); 210e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 211e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int maxDim = Math.max(maxDims.x, maxDims.y); 2128b4836085b928f0ada3b3e6e1d1fd0530ffd6d57Michael Jurka int minDim = Math.max(minDims.x, minDims.y); 213e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 214e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { 215e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Point realSize = new Point(); 216e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka windowManager.getDefaultDisplay().getRealSize(realSize); 217e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka maxDim = Math.max(realSize.x, realSize.y); 2188b4836085b928f0ada3b3e6e1d1fd0530ffd6d57Michael Jurka minDim = Math.min(realSize.x, realSize.y); 219e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 220e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 221e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // We need to ensure that there is enough extra space in the wallpaper 222e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // for the intended 223e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // parallax effects 224e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int defaultWidth, defaultHeight; 225e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (isScreenLarge(res)) { 226e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim)); 227e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka defaultHeight = maxDim; 228e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 229e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim); 230e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka defaultHeight = maxDim; 231e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 232e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return new Point(defaultWidth, defaultHeight); 233e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 234e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 23569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka public static int getRotationFromExif(String path) { 23669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return getRotationFromExifHelper(path, null, 0, null, null); 23769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 23869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 23969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka public static int getRotationFromExif(Context context, Uri uri) { 24069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return getRotationFromExifHelper(null, null, 0, context, uri); 24169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 24269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 24369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka public static int getRotationFromExif(Resources res, int resId) { 24469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return getRotationFromExifHelper(null, res, resId, null, null); 24569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 24669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 24769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private static int getRotationFromExifHelper( 24869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka String path, Resources res, int resId, Context context, Uri uri) { 24969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka ExifInterface ei = new ExifInterface(); 25069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka try { 25169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (path != null) { 25269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka ei.readExif(path); 25369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } else if (uri != null) { 25469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka InputStream is = context.getContentResolver().openInputStream(uri); 25569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka BufferedInputStream bis = new BufferedInputStream(is); 25669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka ei.readExif(bis); 2577b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka bis.close(); 25869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } else { 25969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka InputStream is = res.openRawResource(resId); 26069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka BufferedInputStream bis = new BufferedInputStream(is); 26169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka ei.readExif(bis); 2627b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka bis.close(); 26369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 26469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION); 26569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (ori != null) { 26669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return ExifInterface.getRotationForOrientationValue(ori.shortValue()); 26769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 26869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } catch (IOException e) { 26969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Log.w(LOGTAG, "Getting exif data failed", e); 27069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 27169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return 0; 27269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 27369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 274e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void setWallpaper(String filePath, final boolean finishActivityWhenDone) { 27569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka int rotation = getRotationFromExif(filePath); 27669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka BitmapCropTask cropTask = new BitmapCropTask( 27769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka this, filePath, null, rotation, 0, 0, true, false, null); 278e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final Point bounds = cropTask.getImageBounds(); 279e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Runnable onEndCrop = new Runnable() { 280e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void run() { 281e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateWallpaperDimensions(bounds.x, bounds.y); 282e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (finishActivityWhenDone) { 283e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka setResult(Activity.RESULT_OK); 284e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka finish(); 285e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 286e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 287e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka }; 288e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropTask.setOnEndRunnable(onEndCrop); 289e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropTask.setNoCrop(true); 290e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropTask.execute(); 291e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 292e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 293e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void cropImageAndSetWallpaper( 294e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Resources res, int resId, final boolean finishActivityWhenDone) { 295e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // crop this image and scale it down to the default wallpaper size for 296e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // this device 29769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka int rotation = getRotationFromExif(res, resId); 298e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Point inSize = mCropView.getSourceDimensions(); 299e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Point outSize = getDefaultWallpaperSize(getResources(), 300e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka getWindowManager()); 301e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka RectF crop = getMaxCropRect( 302e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka inSize.x, inSize.y, outSize.x, outSize.y, false); 303e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Runnable onEndCrop = new Runnable() { 304e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void run() { 305e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Passing 0, 0 will cause launcher to revert to using the 306e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // default wallpaper size 307e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateWallpaperDimensions(0, 0); 308e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (finishActivityWhenDone) { 309e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka setResult(Activity.RESULT_OK); 310e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka finish(); 311e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 312e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 313e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka }; 314e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka BitmapCropTask cropTask = new BitmapCropTask(this, res, resId, 31569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka crop, rotation, outSize.x, outSize.y, true, false, onEndCrop); 316e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropTask.execute(); 317e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 318e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 319e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static boolean isScreenLarge(Resources res) { 320e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Configuration config = res.getConfiguration(); 321e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return config.smallestScreenWidthDp >= 720; 322e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 323e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 324e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void cropImageAndSetWallpaper(Uri uri, 325e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) { 326e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka // Get the crop 327e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; 328e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka 329e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Point minDims = new Point(); 330e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Point maxDims = new Point(); 331e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Display d = getWindowManager().getDefaultDisplay(); 332e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka d.getCurrentSizeRange(minDims, maxDims); 333e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 334e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Point displaySize = new Point(); 335e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka d.getSize(displaySize); 336e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 337e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int maxDim = Math.max(maxDims.x, maxDims.y); 338e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int minDim = Math.min(minDims.x, minDims.y); 339e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka int defaultWallpaperWidth; 340e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (isScreenLarge(getResources())) { 341e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka defaultWallpaperWidth = (int) (maxDim * 342e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka wallpaperTravelToScreenWidthRatio(maxDim, minDim)); 343e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 344e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka defaultWallpaperWidth = Math.max((int) 345e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka (minDim * WALLPAPER_SCREENS_SPAN), maxDim); 346e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 347e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 348e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean isPortrait = displaySize.x < displaySize.y; 349e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int portraitHeight; 350e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (isPortrait) { 351e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka portraitHeight = mCropView.getHeight(); 352e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 353e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // TODO: how to actually get the proper portrait height? 354e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // This is not quite right: 355e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka portraitHeight = Math.max(maxDims.x, maxDims.y); 356e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 357e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (android.os.Build.VERSION.SDK_INT >= 358e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { 359e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Point realSize = new Point(); 360e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka d.getRealSize(realSize); 361e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka portraitHeight = Math.max(realSize.x, realSize.y); 362e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 363e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Get the crop 364e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka RectF cropRect = mCropView.getCrop(); 36569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka int cropRotation = mCropView.getImageRotation(); 366e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float cropScale = mCropView.getWidth() / (float) cropRect.width(); 367e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 36869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Point inSize = mCropView.getSourceDimensions(); 36969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix rotateMatrix = new Matrix(); 37069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotateMatrix.setRotate(cropRotation); 37169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float[] rotatedInSize = new float[] { inSize.x, inSize.y }; 37269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotateMatrix.mapPoints(rotatedInSize); 37369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotatedInSize[0] = Math.abs(rotatedInSize[0]); 37469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotatedInSize[1] = Math.abs(rotatedInSize[1]); 37569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 376e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // ADJUST CROP WIDTH 377e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Extend the crop all the way to the right, for parallax 378e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka // (or all the way to the left, in RTL) 37969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left; 380e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Cap the amount of extra width 381e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka float maxExtraSpace = defaultWallpaperWidth / cropScale - cropRect.width(); 382e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka extraSpace = Math.min(extraSpace, maxExtraSpace); 383e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 384e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka if (ltr) { 385e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka cropRect.right += extraSpace; 386e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } else { 387e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka cropRect.left -= extraSpace; 388e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } 389e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 390e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // ADJUST CROP HEIGHT 391e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (isPortrait) { 392e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.bottom = cropRect.top + portraitHeight / cropScale; 393e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { // LANDSCAPE 394e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float extraPortraitHeight = 395e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka portraitHeight / cropScale - cropRect.height(); 396e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float expandHeight = 39769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top), 398e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka extraPortraitHeight / 2); 399e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.top -= expandHeight; 400e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.bottom += expandHeight; 401e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 402e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int outWidth = (int) Math.round(cropRect.width() * cropScale); 403e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int outHeight = (int) Math.round(cropRect.height() * cropScale); 404e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 405e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Runnable onEndCrop = new Runnable() { 406e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void run() { 407e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka updateWallpaperDimensions(outWidth, outHeight); 408e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (finishActivityWhenDone) { 409e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka setResult(Activity.RESULT_OK); 410e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka finish(); 411e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 412e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 413e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka }; 414e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka BitmapCropTask cropTask = new BitmapCropTask(this, uri, 41569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop); 416e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (onBitmapCroppedHandler != null) { 417e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropTask.setOnBitmapCropped(onBitmapCroppedHandler); 418e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 419e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropTask.execute(); 420e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 421e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 422e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public interface OnBitmapCroppedHandler { 423e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void onBitmapCropped(byte[] imageBytes); 424e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 425e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 426e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka protected static class BitmapCropTask extends AsyncTask<Void, Void, Boolean> { 427e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Uri mInUri = null; 428e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Context mContext; 429e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka String mInFilePath; 430e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka byte[] mInImageBytes; 431e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int mInResId = 0; 432e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka RectF mCropBounds = null; 433e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int mOutWidth, mOutHeight; 43469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka int mRotation; 435e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka String mOutputFormat = "jpg"; // for now 436e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean mSetWallpaper; 437e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean mSaveCroppedBitmap; 438e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Bitmap mCroppedBitmap; 439e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Runnable mOnEndRunnable; 440e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Resources mResources; 441e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka OnBitmapCroppedHandler mOnBitmapCroppedHandler; 442e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean mNoCrop; 443e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 444e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public BitmapCropTask(Context c, String filePath, 44569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka RectF cropBounds, int rotation, int outWidth, int outHeight, 446e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 447e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mContext = c; 448e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mInFilePath = filePath; 44969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka init(cropBounds, rotation, 45069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); 451e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 452e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 453e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public BitmapCropTask(byte[] imageBytes, 45469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka RectF cropBounds, int rotation, int outWidth, int outHeight, 455e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 456e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mInImageBytes = imageBytes; 45769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka init(cropBounds, rotation, 45869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); 459e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 460e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 461e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka public BitmapCropTask(Context c, Uri inUri, 46269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka RectF cropBounds, int rotation, int outWidth, int outHeight, 463e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 464e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka mContext = c; 465e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mInUri = inUri; 46669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka init(cropBounds, rotation, 46769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); 468e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 469e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 470e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka public BitmapCropTask(Context c, Resources res, int inResId, 47169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka RectF cropBounds, int rotation, int outWidth, int outHeight, 472e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 473e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka mContext = c; 474e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mInResId = inResId; 475e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mResources = res; 47669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka init(cropBounds, rotation, 47769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); 478e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 479e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 48069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private void init(RectF cropBounds, int rotation, int outWidth, int outHeight, 481e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 482e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mCropBounds = cropBounds; 48369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRotation = rotation; 484e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOutWidth = outWidth; 485e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOutHeight = outHeight; 486e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mSetWallpaper = setWallpaper; 487e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mSaveCroppedBitmap = saveCroppedBitmap; 488e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOnEndRunnable = onEndRunnable; 489e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 490e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 491e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setOnBitmapCropped(OnBitmapCroppedHandler handler) { 492e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOnBitmapCroppedHandler = handler; 493e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 494e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 495e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setNoCrop(boolean value) { 496e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mNoCrop = value; 497e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 498e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 499e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setOnEndRunnable(Runnable onEndRunnable) { 500e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOnEndRunnable = onEndRunnable; 501e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 502e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 503e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Helper to setup input stream 5047b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka private InputStream regenerateInputStream() { 505e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) { 506e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " + 507e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka "image byte array given"); 508e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 509e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 510e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mInUri != null) { 5117b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka return new BufferedInputStream( 512e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka mContext.getContentResolver().openInputStream(mInUri)); 513e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else if (mInFilePath != null) { 5147b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka return mContext.openFileInput(mInFilePath); 515e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else if (mInImageBytes != null) { 5167b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes)); 517e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 5187b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka return new BufferedInputStream(mResources.openRawResource(mInResId)); 519e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 520e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } catch (FileNotFoundException e) { 521e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e); 522e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 523e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 5247b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka return null; 525e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 526e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 527e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Point getImageBounds() { 5287b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka InputStream is = regenerateInputStream(); 5297b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (is != null) { 530e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka BitmapFactory.Options options = new BitmapFactory.Options(); 531e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka options.inJustDecodeBounds = true; 5327b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka BitmapFactory.decodeStream(is, null, options); 5337b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Utils.closeSilently(is); 534e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (options.outWidth != 0 && options.outHeight != 0) { 535e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return new Point(options.outWidth, options.outHeight); 536e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 537e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 538e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return null; 539e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 540e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 541e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setCropBounds(RectF cropBounds) { 542e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mCropBounds = cropBounds; 543e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 544e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 545e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Bitmap getCroppedBitmap() { 546e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mCroppedBitmap; 547e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 548e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public boolean cropBitmap() { 549e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean failure = false; 550e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 551e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 552e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka WallpaperManager wallpaperManager = null; 553e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka if (mSetWallpaper) { 554e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext()); 555e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } 5567b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka 5577b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka 5587b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (mSetWallpaper && mNoCrop) { 559e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 5607b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka InputStream is = regenerateInputStream(); 5617b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (is != null) { 5627b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka wallpaperManager.setStream(is); 5637b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Utils.closeSilently(is); 5647b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } 565e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } catch (IOException e) { 566e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(LOGTAG, "cannot write stream to wallpaper", e); 567e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka failure = true; 568e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 569e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return !failure; 5707b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } else { 571e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Find crop bounds (scaled to original image size) 572e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Rect roundedTrueCrop = new Rect(); 57369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix rotateMatrix = new Matrix(); 57469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix inverseRotateMatrix = new Matrix(); 57569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (mRotation > 0) { 57669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotateMatrix.setRotate(mRotation); 57769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka inverseRotateMatrix.setRotate(-mRotation); 57869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 57969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.roundOut(roundedTrueCrop); 58069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds = new RectF(roundedTrueCrop); 58169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 58269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Point bounds = getImageBounds(); 5837b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (bounds == null) { 5847b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Log.w(LOGTAG, "cannot get bounds for image"); 5857b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka failure = true; 5867b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka return false; 5877b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } 58869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 58969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float[] rotatedBounds = new float[] { bounds.x, bounds.y }; 59069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotateMatrix.mapPoints(rotatedBounds); 59169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotatedBounds[0] = Math.abs(rotatedBounds[0]); 59269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotatedBounds[1] = Math.abs(rotatedBounds[1]); 59369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 59469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2); 59569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka inverseRotateMatrix.mapRect(mCropBounds); 59669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.offset(bounds.x/2, bounds.y/2); 59769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 59869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 59969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 600e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mCropBounds.roundOut(roundedTrueCrop); 601e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 602e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { 603e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(LOGTAG, "crop has bad values for full size image"); 604e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka failure = true; 605e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return false; 606e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 607e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 608e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // See how much we're reducing the size of the image 609e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / mOutWidth, 610e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka roundedTrueCrop.height() / mOutHeight); 611e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 612e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Attempt to open a region decoder 613e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka BitmapRegionDecoder decoder = null; 614e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 6157b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka InputStream is = regenerateInputStream(); 6167b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (is == null) { 6177b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString()); 6187b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka failure = true; 6197b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka return false; 6207b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } 6217b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka decoder = BitmapRegionDecoder.newInstance(is, false); 6227b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Utils.closeSilently(is); 623e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } catch (IOException e) { 624e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e); 625e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 626e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 627e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Bitmap crop = null; 628e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (decoder != null) { 629e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Do region decoding to get crop bitmap 630e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka BitmapFactory.Options options = new BitmapFactory.Options(); 631e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (scaleDownSampleSize > 1) { 632e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka options.inSampleSize = scaleDownSampleSize; 633e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 634e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka crop = decoder.decodeRegion(roundedTrueCrop, options); 635e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka decoder.recycle(); 636e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 637e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 638e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (crop == null) { 639e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // BitmapRegionDecoder has failed, try to crop in-memory 6407b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka InputStream is = regenerateInputStream(); 641e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Bitmap fullSize = null; 6427b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (is != null) { 643e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka BitmapFactory.Options options = new BitmapFactory.Options(); 644e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (scaleDownSampleSize > 1) { 645e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka options.inSampleSize = scaleDownSampleSize; 646e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 6477b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka fullSize = BitmapFactory.decodeStream(is, null, options); 6487b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Utils.closeSilently(is); 649e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 650e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (fullSize != null) { 65169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.left /= scaleDownSampleSize; 65269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.top /= scaleDownSampleSize; 65369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.bottom /= scaleDownSampleSize; 65469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.right /= scaleDownSampleSize; 65569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.roundOut(roundedTrueCrop); 65669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 657e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, 658e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka roundedTrueCrop.top, roundedTrueCrop.width(), 659e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka roundedTrueCrop.height()); 660e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 661e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 662e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 663e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (crop == null) { 664e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(LOGTAG, "cannot decode file: " + mInUri.toString()); 665e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka failure = true; 666e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return false; 667e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 66869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) { 66969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() }; 67069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotateMatrix.mapPoints(dimsAfter); 67169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka dimsAfter[0] = Math.abs(dimsAfter[0]); 67269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka dimsAfter[1] = Math.abs(dimsAfter[1]); 67369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 67469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (!(mOutWidth > 0 && mOutHeight > 0)) { 67569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mOutWidth = Math.round(dimsAfter[0]); 67669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mOutHeight = Math.round(dimsAfter[1]); 677e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 67869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 67969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]); 680e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight); 68169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 68269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix m = new Matrix(); 68369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (mRotation == 0) { 68469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); 68569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } else { 68669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix m1 = new Matrix(); 68769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f); 68869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix m2 = new Matrix(); 68969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka m2.setRotate(mRotation); 69069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix m3 = new Matrix(); 69169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f); 69269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix m4 = new Matrix(); 69369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); 69469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 69569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix c1 = new Matrix(); 69669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka c1.setConcat(m2, m1); 69769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix c2 = new Matrix(); 69869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka c2.setConcat(m4, m3); 69969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka m.setConcat(c2, c1); 70069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 70169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 702e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), 703e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka (int) returnRect.height(), Bitmap.Config.ARGB_8888); 704e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tmp != null) { 705e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Canvas c = new Canvas(tmp); 706e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka Paint p = new Paint(); 707e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka p.setFilterBitmap(true); 708e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka c.drawBitmap(crop, m, p); 709e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka crop = tmp; 710e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 711e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 712e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 713e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mSaveCroppedBitmap) { 714e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mCroppedBitmap = crop; 715e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 716e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 717e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Get output compression format 718e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka CompressFormat cf = 719e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka convertExtensionToCompressFormat(getFileExtension(mOutputFormat)); 720e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 721e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Compress to byte array 722e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048); 723e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) { 724e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // If we need to set to the wallpaper, set it 725e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka if (mSetWallpaper && wallpaperManager != null) { 726e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka try { 727e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka byte[] outByteArray = tmpOut.toByteArray(); 728e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka wallpaperManager.setStream(new ByteArrayInputStream(outByteArray)); 729e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka if (mOnBitmapCroppedHandler != null) { 730e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka mOnBitmapCroppedHandler.onBitmapCropped(outByteArray); 731e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 732e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } catch (IOException e) { 733e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka Log.w(LOGTAG, "cannot write stream to wallpaper", e); 734e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka failure = true; 735e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 736e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 737e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 738e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(LOGTAG, "cannot compress bitmap"); 739e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka failure = true; 740e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 741e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 742e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return !failure; // True if any of the operations failed 743e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 744e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 745e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 746e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected Boolean doInBackground(Void... params) { 747e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return cropBitmap(); 748e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 749e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 750e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 751e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void onPostExecute(Boolean result) { 752e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka if (mOnEndRunnable != null) { 753e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka mOnEndRunnable.run(); 754e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } 755e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 756e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 757e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 758e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void updateWallpaperDimensions(int width, int height) { 759e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka String spKey = getSharedPreferencesKey(); 760e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE); 761e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka SharedPreferences.Editor editor = sp.edit(); 762e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (width != 0 && height != 0) { 763e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka editor.putInt(WALLPAPER_WIDTH_KEY, width); 764e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka editor.putInt(WALLPAPER_HEIGHT_KEY, height); 765e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 766e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka editor.remove(WALLPAPER_WIDTH_KEY); 767e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka editor.remove(WALLPAPER_HEIGHT_KEY); 768e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 769e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka editor.commit(); 770e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 771e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka suggestWallpaperDimension(getResources(), 772e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka sp, getWindowManager(), WallpaperManager.getInstance(this)); 773e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 774e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 775e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka static public void suggestWallpaperDimension(Resources res, 776e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final SharedPreferences sharedPrefs, 777e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka WindowManager windowManager, 778e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final WallpaperManager wallpaperManager) { 77969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka final Point defaultWallpaperSize = getDefaultWallpaperSize(res, windowManager); 780e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 781e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka new Thread("suggestWallpaperDimension") { 782e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void run() { 783e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // If we have saved a wallpaper width/height, use that instead 784e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, defaultWallpaperSize.x); 785e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, defaultWallpaperSize.y); 786e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight); 787e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 788e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka }.start(); 789e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 790e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 791e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected static RectF getMaxCropRect( 792e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) { 793e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka RectF cropRect = new RectF(); 794e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Get a crop rect that will fit this 795e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (inWidth / (float) inHeight > outWidth / (float) outHeight) { 796e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.top = 0; 797e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.bottom = inHeight; 798e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2; 799e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.right = inWidth - cropRect.left; 800e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (leftAligned) { 801e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.right -= cropRect.left; 802e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.left = 0; 803e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 804e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 805e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.left = 0; 806e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.right = inWidth; 807e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2; 808e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.bottom = inHeight - cropRect.top; 809e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 810e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return cropRect; 811e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 812e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 813e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected static CompressFormat convertExtensionToCompressFormat(String extension) { 814e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG; 815e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 816e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 817e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected static String getFileExtension(String requestFormat) { 818e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka String outputFormat = (requestFormat == null) 819e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ? "jpg" 820e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka : requestFormat; 821e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka outputFormat = outputFormat.toLowerCase(); 822e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return (outputFormat.equals("png") || outputFormat.equals("gif")) 823e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ? "png" // We don't support gif compression. 824e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka : "jpg"; 825e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 826e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka} 827