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.res.Configuration; 25e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.content.res.Resources; 26e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Bitmap; 27e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Bitmap.CompressFormat; 28e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.BitmapFactory; 29e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.BitmapRegionDecoder; 30e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Canvas; 31e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Matrix; 32e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Paint; 33e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Point; 34e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.Rect; 35e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.graphics.RectF; 36e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.net.Uri; 37e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.os.AsyncTask; 38e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.os.Bundle; 39e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.util.Log; 40e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.Display; 41e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.View; 42e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport android.view.WindowManager; 437b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurkaimport android.widget.Toast; 44e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 45e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.gallery3d.common.Utils; 4669784065ea78550944b7eddb559c0dac952e20e8Michael Jurkaimport com.android.gallery3d.exif.ExifInterface; 47e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport com.android.photos.BitmapRegionTileSource; 487b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurkaimport com.android.photos.BitmapRegionTileSource.BitmapSource; 49e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 50e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.BufferedInputStream; 51e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.ByteArrayInputStream; 52e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.ByteArrayOutputStream; 53e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.FileNotFoundException; 54e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.IOException; 55e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkaimport java.io.InputStream; 56e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 57e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurkapublic class WallpaperCropActivity extends Activity { 58e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final String LOGTAG = "Launcher3.CropActivity"; 59e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 60e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected static final String WALLPAPER_WIDTH_KEY = "wallpaper.width"; 61e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height"; 62e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final int DEFAULT_COMPRESS_QUALITY = 90; 63e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka /** 64e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * The maximum bitmap size we allow to be returned through the intent. 65e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * Intents have a maximum of 1MB in total size. However, the Bitmap seems to 66e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * have some overhead to hit so that we go way below the limit here to make 67e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * sure the intent stays below 1MB.We should consider just returning a byte 68e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka * array instead of a Bitmap instance to avoid overhead. 69e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka */ 70e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public static final int MAX_BMAP_IN_INTENT = 750000; 71e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static final float WALLPAPER_SCREENS_SPAN = 2f; 72e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 7335f922d1d038d14f94b584baae324513610f7813Michael Jurka protected static Point sDefaultWallpaperSize; 7435f922d1d038d14f94b584baae324513610f7813Michael Jurka 75e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected CropView mCropView; 76e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected Uri mUri; 770fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek private View mSetWallpaperButton; 78e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 79e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 80e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void onCreate(Bundle savedInstanceState) { 81e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka super.onCreate(savedInstanceState); 82e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka init(); 83e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka if (!enableRotation()) { 84e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT); 85e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } 86e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 87e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 88e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void init() { 89e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka setContentView(R.layout.wallpaper_cropper); 90e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 9151efddbd3bb304de2dd47fa8cd1114ac555958bbAlan Viverette mCropView = findViewById(R.id.cropView); 92e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 9369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Intent cropIntent = getIntent(); 94e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final Uri imageUri = cropIntent.getData(); 95e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 9669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (imageUri == null) { 9769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Log.e(LOGTAG, "No URI passed in intent, exiting WallpaperCropActivity"); 9869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka finish(); 9969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return; 10069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 10169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 102e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Action bar 103e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Show the custom action bar view 104e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final ActionBar actionBar = getActionBar(); 105e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka actionBar.setCustomView(R.layout.actionbar_set_wallpaper); 106e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka actionBar.getCustomView().setOnClickListener( 107e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka new View.OnClickListener() { 108e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 109e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void onClick(View v) { 110e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean finishActivityWhenDone = true; 111e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone); 112e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 113e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka }); 1140fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek mSetWallpaperButton = findViewById(R.id.set_wallpaper_button); 1155271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka 1165271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka // Load image in background 1177b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka final BitmapRegionTileSource.UriBitmapSource bitmapSource = 1187b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024); 1190fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek mSetWallpaperButton.setVisibility(View.INVISIBLE); 1207b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Runnable onLoad = new Runnable() { 1217b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka public void run() { 1227b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) { 1237b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Toast.makeText(WallpaperCropActivity.this, 1247b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka getString(R.string.wallpaper_load_fail), 1257b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Toast.LENGTH_LONG).show(); 1267b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka finish(); 1270fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek } else { 1280fa6c1b146f7ed1aba335c30878e2c1b2bf2a8f1Selim Cinek mSetWallpaperButton.setVisibility(View.VISIBLE); 1297b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } 1307b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } 1317b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka }; 1327b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka setCropViewTileSource(bitmapSource, true, false, onLoad); 1335271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1345271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka 135fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal @Override 136fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal protected void onDestroy() { 137fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal if (mCropView != null) { 138fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal mCropView.destroy(); 139fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal } 140fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal super.onDestroy(); 141fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal } 142fabf1f23d64d717790e68b78ad75e1466426be65Sunny Goyal 1437b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka public void setCropViewTileSource( 1447b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka final BitmapRegionTileSource.BitmapSource bitmapSource, final boolean touchEnabled, 1457b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka final boolean moveToLeft, final Runnable postExecute) { 1465271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka final Context context = WallpaperCropActivity.this; 1475271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka final View progressView = findViewById(R.id.loading); 1485271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() { 1495271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka protected Void doInBackground(Void...args) { 1505271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka if (!isCancelled()) { 151d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek try { 152d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek bitmapSource.loadInBackground(); 153d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek } catch (SecurityException securityException) { 154d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek if (isDestroyed()) { 155d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek // Temporarily granted permissions are revoked when the activity 156d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek // finishes, potentially resulting in a SecurityException here. 157d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek // Even though {@link #isDestroyed} might also return true in different 158d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek // situations where the configuration changes, we are fine with 159d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek // catching these cases here as well. 160d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek cancel(false); 161d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek } else { 162d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek // otherwise it had a different cause and we throw it further 163d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek throw securityException; 164d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek } 165d6901a95d8aabc25fdd108d3605b34f848ab10e0Selim Cinek } 1665271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1675271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka return null; 1685271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1695271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka protected void onPostExecute(Void arg) { 1705271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka if (!isCancelled()) { 1715271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka progressView.setVisibility(View.INVISIBLE); 1727b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) { 1737b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka mCropView.setTileSource( 1747b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka new BitmapRegionTileSource(context, bitmapSource), null); 1757b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka mCropView.setTouchEnabled(touchEnabled); 1767b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (moveToLeft) { 1777b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka mCropView.moveToLeft(); 1787b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } 1795271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1805271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1817b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (postExecute != null) { 1827b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka postExecute.run(); 1837b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } 1845271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1855271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka }; 1865271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka // We don't want to show the spinner every time we load an image, because that would be 1875271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka // annoying; instead, only start showing the spinner if loading the image has taken 1885271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka // longer than 1 sec (ie 1000 ms) 1895271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka progressView.postDelayed(new Runnable() { 1905271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka public void run() { 1915271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka if (loadBitmapTask.getStatus() != AsyncTask.Status.FINISHED) { 1925271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka progressView.setVisibility(View.VISIBLE); 1935271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1945271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka } 1955271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka }, 1000); 1965271ea16c1989f99c0db7ab80b2b321441f60023Michael Jurka loadBitmapTask.execute(); 197e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } 198e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka 199e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka public boolean enableRotation() { 200e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka return getResources().getBoolean(R.bool.allow_rotation); 201e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 202e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 203e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public static String getSharedPreferencesKey() { 204e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return WallpaperCropActivity.class.getName(); 205e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 206e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 207e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // As a ratio of screen height, the total distance we want the parallax effect to span 208e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // horizontally 209e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static float wallpaperTravelToScreenWidthRatio(int width, int height) { 210e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float aspectRatio = width / (float) height; 211e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 212e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width 213e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width 214e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // We will use these two data points to extrapolate how much the wallpaper parallax effect 215e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // to span (ie travel) at any aspect ratio: 216e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 217e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float ASPECT_RATIO_LANDSCAPE = 16/10f; 218e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float ASPECT_RATIO_PORTRAIT = 10/16f; 219e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f; 220e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f; 221e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 222e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // To find out the desired width at different aspect ratios, we use the following two 223e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // formulas, where the coefficient on x is the aspect ratio (width/height): 224e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // (16/10)x + y = 1.5 225e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // (10/16)x + y = 1.2 226e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // We solve for x and y and end up with a final formula: 227e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float x = 228e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) / 229e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT); 230e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT; 231e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return x * aspectRatio + y; 232e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 233e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 234e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka static protected Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) { 23535f922d1d038d14f94b584baae324513610f7813Michael Jurka if (sDefaultWallpaperSize == null) { 23635f922d1d038d14f94b584baae324513610f7813Michael Jurka Point minDims = new Point(); 23735f922d1d038d14f94b584baae324513610f7813Michael Jurka Point maxDims = new Point(); 23835f922d1d038d14f94b584baae324513610f7813Michael Jurka windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims); 23935f922d1d038d14f94b584baae324513610f7813Michael Jurka 24035f922d1d038d14f94b584baae324513610f7813Michael Jurka int maxDim = Math.max(maxDims.x, maxDims.y); 24135f922d1d038d14f94b584baae324513610f7813Michael Jurka int minDim = Math.max(minDims.x, minDims.y); 24235f922d1d038d14f94b584baae324513610f7813Michael Jurka 24335f922d1d038d14f94b584baae324513610f7813Michael Jurka if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { 24435f922d1d038d14f94b584baae324513610f7813Michael Jurka Point realSize = new Point(); 24535f922d1d038d14f94b584baae324513610f7813Michael Jurka windowManager.getDefaultDisplay().getRealSize(realSize); 24635f922d1d038d14f94b584baae324513610f7813Michael Jurka maxDim = Math.max(realSize.x, realSize.y); 24735f922d1d038d14f94b584baae324513610f7813Michael Jurka minDim = Math.min(realSize.x, realSize.y); 24835f922d1d038d14f94b584baae324513610f7813Michael Jurka } 249e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 25035f922d1d038d14f94b584baae324513610f7813Michael Jurka // We need to ensure that there is enough extra space in the wallpaper 25135f922d1d038d14f94b584baae324513610f7813Michael Jurka // for the intended parallax effects 25235f922d1d038d14f94b584baae324513610f7813Michael Jurka final int defaultWidth, defaultHeight; 25335f922d1d038d14f94b584baae324513610f7813Michael Jurka if (isScreenLarge(res)) { 25435f922d1d038d14f94b584baae324513610f7813Michael Jurka defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim)); 25535f922d1d038d14f94b584baae324513610f7813Michael Jurka defaultHeight = maxDim; 25635f922d1d038d14f94b584baae324513610f7813Michael Jurka } else { 25735f922d1d038d14f94b584baae324513610f7813Michael Jurka defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim); 25835f922d1d038d14f94b584baae324513610f7813Michael Jurka defaultHeight = maxDim; 25935f922d1d038d14f94b584baae324513610f7813Michael Jurka } 26035f922d1d038d14f94b584baae324513610f7813Michael Jurka sDefaultWallpaperSize = new Point(defaultWidth, defaultHeight); 261e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 26235f922d1d038d14f94b584baae324513610f7813Michael Jurka return sDefaultWallpaperSize; 263e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 264e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 26569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka public static int getRotationFromExif(String path) { 26669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return getRotationFromExifHelper(path, null, 0, null, null); 26769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 26869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 26969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka public static int getRotationFromExif(Context context, Uri uri) { 27069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return getRotationFromExifHelper(null, null, 0, context, uri); 27169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 27269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 27369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka public static int getRotationFromExif(Resources res, int resId) { 27469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return getRotationFromExifHelper(null, res, resId, null, null); 27569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 27669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 27769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private static int getRotationFromExifHelper( 27869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka String path, Resources res, int resId, Context context, Uri uri) { 27969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka ExifInterface ei = new ExifInterface(); 280b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka InputStream is = null; 281b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka BufferedInputStream bis = null; 28269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka try { 28369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (path != null) { 28469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka ei.readExif(path); 28569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } else if (uri != null) { 286b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka is = context.getContentResolver().openInputStream(uri); 287b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka bis = new BufferedInputStream(is); 28869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka ei.readExif(bis); 28969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } else { 290b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka is = res.openRawResource(resId); 291b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka bis = new BufferedInputStream(is); 29269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka ei.readExif(bis); 29369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 29469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION); 29569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (ori != null) { 29669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return ExifInterface.getRotationForOrientationValue(ori.shortValue()); 29769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 29869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } catch (IOException e) { 29969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Log.w(LOGTAG, "Getting exif data failed", e); 3005ab7d37d903f336c5a96f8caca9b36439e76bdb4Selim Cinek } catch (NullPointerException e) { 3015ab7d37d903f336c5a96f8caca9b36439e76bdb4Selim Cinek // Sometimes the ExifInterface has an internal NPE if Exif data isn't valid 3025ab7d37d903f336c5a96f8caca9b36439e76bdb4Selim Cinek Log.w(LOGTAG, "Getting exif data failed", e); 303b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka } finally { 304b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka Utils.closeSilently(bis); 305b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka Utils.closeSilently(is); 30669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 30769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka return 0; 30869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 30969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 310e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void setWallpaper(String filePath, final boolean finishActivityWhenDone) { 31169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka int rotation = getRotationFromExif(filePath); 31269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka BitmapCropTask cropTask = new BitmapCropTask( 31369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka this, filePath, null, rotation, 0, 0, true, false, null); 314e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final Point bounds = cropTask.getImageBounds(); 315e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Runnable onEndCrop = new Runnable() { 316e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void run() { 317e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (finishActivityWhenDone) { 318e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka setResult(Activity.RESULT_OK); 319e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka finish(); 320e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 321e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 322e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka }; 323e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropTask.setOnEndRunnable(onEndCrop); 324e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropTask.setNoCrop(true); 325e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropTask.execute(); 326e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 327e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 328e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void cropImageAndSetWallpaper( 329e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Resources res, int resId, final boolean finishActivityWhenDone) { 330e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // crop this image and scale it down to the default wallpaper size for 331e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // this device 33269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka int rotation = getRotationFromExif(res, resId); 333e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Point inSize = mCropView.getSourceDimensions(); 334e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Point outSize = getDefaultWallpaperSize(getResources(), 335e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka getWindowManager()); 336e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka RectF crop = getMaxCropRect( 337e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka inSize.x, inSize.y, outSize.x, outSize.y, false); 338e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Runnable onEndCrop = new Runnable() { 339e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void run() { 340e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (finishActivityWhenDone) { 341e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka setResult(Activity.RESULT_OK); 342e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka finish(); 343e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 344e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 345e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka }; 346e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka BitmapCropTask cropTask = new BitmapCropTask(this, res, resId, 34769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka crop, rotation, outSize.x, outSize.y, true, false, onEndCrop); 348e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropTask.execute(); 349e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 350e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 351e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka private static boolean isScreenLarge(Resources res) { 352e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Configuration config = res.getConfiguration(); 353e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return config.smallestScreenWidthDp >= 720; 354e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 355e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 356e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void cropImageAndSetWallpaper(Uri uri, 357e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) { 3582ab14daebac46307d2364733481edc3115bbf98cMichael Jurka boolean centerCrop = getResources().getBoolean(R.bool.center_crop); 359e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka // Get the crop 360e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; 361e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka 362e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Display d = getWindowManager().getDefaultDisplay(); 363e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 364e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Point displaySize = new Point(); 365e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka d.getSize(displaySize); 366e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean isPortrait = displaySize.x < displaySize.y; 367eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka 368eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka Point defaultWallpaperSize = getDefaultWallpaperSize(getResources(), 369eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka getWindowManager()); 370e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Get the crop 371e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka RectF cropRect = mCropView.getCrop(); 372912e0df89ce798a5922f87fd65b87bb99f8b4813Selim Cinek 373c41853cee89b391cd2d4d93205ea69c746ccfa4cSelim Cinek Point inSize = mCropView.getSourceDimensions(); 374c41853cee89b391cd2d4d93205ea69c746ccfa4cSelim Cinek 37569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka int cropRotation = mCropView.getImageRotation(); 376e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float cropScale = mCropView.getWidth() / (float) cropRect.width(); 377e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 37869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix rotateMatrix = new Matrix(); 37969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotateMatrix.setRotate(cropRotation); 38069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float[] rotatedInSize = new float[] { inSize.x, inSize.y }; 38169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotateMatrix.mapPoints(rotatedInSize); 38269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotatedInSize[0] = Math.abs(rotatedInSize[0]); 38369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotatedInSize[1] = Math.abs(rotatedInSize[1]); 38469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 3857718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek // Due to rounding errors in the cropview renderer the edges can be slightly offset 3867718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek // therefore we ensure that the boundaries are sanely defined 3877718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek cropRect.left = Math.max(0, cropRect.left); 3887718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek cropRect.right = Math.min(rotatedInSize[0], cropRect.right); 3897718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek cropRect.top = Math.max(0, cropRect.top); 3907718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek cropRect.bottom = Math.min(rotatedInSize[1], cropRect.bottom); 3917718fc1291c73576417f0c25d88f5105f7ae7365Selim Cinek 392e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // ADJUST CROP WIDTH 393e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Extend the crop all the way to the right, for parallax 394e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka // (or all the way to the left, in RTL) 3952ab14daebac46307d2364733481edc3115bbf98cMichael Jurka float extraSpace; 3962ab14daebac46307d2364733481edc3115bbf98cMichael Jurka if (centerCrop) { 3972ab14daebac46307d2364733481edc3115bbf98cMichael Jurka extraSpace = 2f * Math.min(rotatedInSize[0] - cropRect.right, cropRect.left); 3982ab14daebac46307d2364733481edc3115bbf98cMichael Jurka } else { 3992ab14daebac46307d2364733481edc3115bbf98cMichael Jurka extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left; 4002ab14daebac46307d2364733481edc3115bbf98cMichael Jurka } 401e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Cap the amount of extra width 402eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka float maxExtraSpace = defaultWallpaperSize.x / cropScale - cropRect.width(); 403e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka extraSpace = Math.min(extraSpace, maxExtraSpace); 404e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 4052ab14daebac46307d2364733481edc3115bbf98cMichael Jurka if (centerCrop) { 4062ab14daebac46307d2364733481edc3115bbf98cMichael Jurka cropRect.left -= extraSpace / 2f; 4072ab14daebac46307d2364733481edc3115bbf98cMichael Jurka cropRect.right += extraSpace / 2f; 408e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } else { 4092ab14daebac46307d2364733481edc3115bbf98cMichael Jurka if (ltr) { 4102ab14daebac46307d2364733481edc3115bbf98cMichael Jurka cropRect.right += extraSpace; 4112ab14daebac46307d2364733481edc3115bbf98cMichael Jurka } else { 4122ab14daebac46307d2364733481edc3115bbf98cMichael Jurka cropRect.left -= extraSpace; 4132ab14daebac46307d2364733481edc3115bbf98cMichael Jurka } 414e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } 415e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 416e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // ADJUST CROP HEIGHT 417e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (isPortrait) { 418eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka cropRect.bottom = cropRect.top + defaultWallpaperSize.y / cropScale; 419e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { // LANDSCAPE 420e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float extraPortraitHeight = 421eed96a0b9088dcce121ffee476b4d7fa72686e77Michael Jurka defaultWallpaperSize.y / cropScale - cropRect.height(); 422e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka float expandHeight = 42369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top), 424e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka extraPortraitHeight / 2); 425e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.top -= expandHeight; 426e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.bottom += expandHeight; 427e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 428e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int outWidth = (int) Math.round(cropRect.width() * cropScale); 429e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka final int outHeight = (int) Math.round(cropRect.height() * cropScale); 430e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 431e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Runnable onEndCrop = new Runnable() { 432e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void run() { 433e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (finishActivityWhenDone) { 434e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka setResult(Activity.RESULT_OK); 435e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka finish(); 436e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 437e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 438e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka }; 439e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka BitmapCropTask cropTask = new BitmapCropTask(this, uri, 44069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop); 441e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (onBitmapCroppedHandler != null) { 442e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropTask.setOnBitmapCropped(onBitmapCroppedHandler); 443e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 444e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropTask.execute(); 445e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 446e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 447e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public interface OnBitmapCroppedHandler { 448e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void onBitmapCropped(byte[] imageBytes); 449e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 450e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 451e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka protected static class BitmapCropTask extends AsyncTask<Void, Void, Boolean> { 452e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Uri mInUri = null; 453e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Context mContext; 454e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka String mInFilePath; 455e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka byte[] mInImageBytes; 456e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int mInResId = 0; 457e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka RectF mCropBounds = null; 458e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int mOutWidth, mOutHeight; 45969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka int mRotation; 460e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka String mOutputFormat = "jpg"; // for now 461e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean mSetWallpaper; 462e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean mSaveCroppedBitmap; 463e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Bitmap mCroppedBitmap; 464e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Runnable mOnEndRunnable; 465e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Resources mResources; 466e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka OnBitmapCroppedHandler mOnBitmapCroppedHandler; 467e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean mNoCrop; 468e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 469e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public BitmapCropTask(Context c, String filePath, 47069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka RectF cropBounds, int rotation, int outWidth, int outHeight, 471e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 472e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mContext = c; 473e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mInFilePath = filePath; 47469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka init(cropBounds, rotation, 47569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); 476e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 477e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 478e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public BitmapCropTask(byte[] imageBytes, 47969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka RectF cropBounds, int rotation, int outWidth, int outHeight, 480e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 481e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mInImageBytes = imageBytes; 48269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka init(cropBounds, rotation, 48369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); 484e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 485e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 486e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka public BitmapCropTask(Context c, Uri inUri, 48769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka RectF cropBounds, int rotation, int outWidth, int outHeight, 488e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 489e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka mContext = c; 490e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mInUri = inUri; 49169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka init(cropBounds, rotation, 49269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); 493e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 494e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 495e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka public BitmapCropTask(Context c, Resources res, int inResId, 49669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka RectF cropBounds, int rotation, int outWidth, int outHeight, 497e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 498e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka mContext = c; 499e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mInResId = inResId; 500e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mResources = res; 50169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka init(cropBounds, rotation, 50269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); 503e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 504e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 50569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka private void init(RectF cropBounds, int rotation, int outWidth, int outHeight, 506e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 507e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mCropBounds = cropBounds; 50869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mRotation = rotation; 509e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOutWidth = outWidth; 510e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOutHeight = outHeight; 511e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mSetWallpaper = setWallpaper; 512e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mSaveCroppedBitmap = saveCroppedBitmap; 513e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOnEndRunnable = onEndRunnable; 514e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 515e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 516e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setOnBitmapCropped(OnBitmapCroppedHandler handler) { 517e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOnBitmapCroppedHandler = handler; 518e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 519e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 520e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setNoCrop(boolean value) { 521e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mNoCrop = value; 522e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 523e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 524e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setOnEndRunnable(Runnable onEndRunnable) { 525e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mOnEndRunnable = onEndRunnable; 526e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 527e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 528e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Helper to setup input stream 5297b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka private InputStream regenerateInputStream() { 530e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) { 531e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " + 532e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka "image byte array given"); 533e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 534e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 535e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mInUri != null) { 5367b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka return new BufferedInputStream( 537e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka mContext.getContentResolver().openInputStream(mInUri)); 538e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else if (mInFilePath != null) { 5397b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka return mContext.openFileInput(mInFilePath); 540e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else if (mInImageBytes != null) { 5417b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes)); 542e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 5437b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka return new BufferedInputStream(mResources.openRawResource(mInResId)); 544e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 545e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } catch (FileNotFoundException e) { 546e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e); 547e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 548e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 5497b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka return null; 550e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 551e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 552e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Point getImageBounds() { 5537b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka InputStream is = regenerateInputStream(); 5547b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (is != null) { 555e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka BitmapFactory.Options options = new BitmapFactory.Options(); 556e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka options.inJustDecodeBounds = true; 5577b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka BitmapFactory.decodeStream(is, null, options); 5587b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Utils.closeSilently(is); 559e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (options.outWidth != 0 && options.outHeight != 0) { 560e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return new Point(options.outWidth, options.outHeight); 561e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 562e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 563e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return null; 564e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 565e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 566e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public void setCropBounds(RectF cropBounds) { 567e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mCropBounds = cropBounds; 568e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 569e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 570e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public Bitmap getCroppedBitmap() { 571e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return mCroppedBitmap; 572e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 573e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka public boolean cropBitmap() { 574e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka boolean failure = false; 575e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 576e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 577e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka WallpaperManager wallpaperManager = null; 578e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka if (mSetWallpaper) { 579e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext()); 580e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } 5817b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka 5827b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka 5837b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (mSetWallpaper && mNoCrop) { 584e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 5857b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka InputStream is = regenerateInputStream(); 5867b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (is != null) { 5877b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka wallpaperManager.setStream(is); 5887b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Utils.closeSilently(is); 5897b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } 590e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } catch (IOException e) { 591e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(LOGTAG, "cannot write stream to wallpaper", e); 592e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka failure = true; 593e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 594e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return !failure; 5957b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } else { 596e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Find crop bounds (scaled to original image size) 597e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Rect roundedTrueCrop = new Rect(); 59869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix rotateMatrix = new Matrix(); 59969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix inverseRotateMatrix = new Matrix(); 600e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka 601e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka Point bounds = getImageBounds(); 60269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (mRotation > 0) { 60369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotateMatrix.setRotate(mRotation); 60469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka inverseRotateMatrix.setRotate(-mRotation); 60569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 60669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.roundOut(roundedTrueCrop); 60769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds = new RectF(roundedTrueCrop); 60869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 6097b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (bounds == null) { 6107b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Log.w(LOGTAG, "cannot get bounds for image"); 6117b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka failure = true; 6127b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka return false; 6137b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } 61469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 61569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float[] rotatedBounds = new float[] { bounds.x, bounds.y }; 61669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotateMatrix.mapPoints(rotatedBounds); 61769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotatedBounds[0] = Math.abs(rotatedBounds[0]); 61869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotatedBounds[1] = Math.abs(rotatedBounds[1]); 61969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 62069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2); 62169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka inverseRotateMatrix.mapRect(mCropBounds); 62269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.offset(bounds.x/2, bounds.y/2); 62369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 62469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 62569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 626e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mCropBounds.roundOut(roundedTrueCrop); 627e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 628e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { 629e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(LOGTAG, "crop has bad values for full size image"); 630e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka failure = true; 631e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return false; 632e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 633e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 634e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // See how much we're reducing the size of the image 635b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth, 636b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka roundedTrueCrop.height() / mOutHeight)); 637e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Attempt to open a region decoder 638e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka BitmapRegionDecoder decoder = null; 639b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka InputStream is = null; 640e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka try { 641b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka is = regenerateInputStream(); 6427b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (is == null) { 6437b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString()); 6447b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka failure = true; 6457b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka return false; 6467b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka } 6477b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka decoder = BitmapRegionDecoder.newInstance(is, false); 6487b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Utils.closeSilently(is); 649e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } catch (IOException e) { 650e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e); 651b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka } finally { 652b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka Utils.closeSilently(is); 653b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka is = null; 654e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 655e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 656e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Bitmap crop = null; 657e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (decoder != null) { 658e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Do region decoding to get crop bitmap 659e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka BitmapFactory.Options options = new BitmapFactory.Options(); 660e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (scaleDownSampleSize > 1) { 661e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka options.inSampleSize = scaleDownSampleSize; 662e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 663e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka crop = decoder.decodeRegion(roundedTrueCrop, options); 664e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka decoder.recycle(); 665e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 666e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 667e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (crop == null) { 668e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // BitmapRegionDecoder has failed, try to crop in-memory 669b2552648cec772c896ff31a7aa52ae48380459c0Michael Jurka is = regenerateInputStream(); 670e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Bitmap fullSize = null; 6717b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka if (is != null) { 672e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka BitmapFactory.Options options = new BitmapFactory.Options(); 673e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (scaleDownSampleSize > 1) { 674e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka options.inSampleSize = scaleDownSampleSize; 675e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 6767b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka fullSize = BitmapFactory.decodeStream(is, null, options); 6777b215cb92288aa0a21bc773511ddd537b8fbb459Michael Jurka Utils.closeSilently(is); 678e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 679e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (fullSize != null) { 680e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka // Find out the true sample size that was used by the decoder 681e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka scaleDownSampleSize = bounds.x / fullSize.getWidth(); 68269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.left /= scaleDownSampleSize; 68369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.top /= scaleDownSampleSize; 68469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.bottom /= scaleDownSampleSize; 68569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.right /= scaleDownSampleSize; 68669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mCropBounds.roundOut(roundedTrueCrop); 68769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 688e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka // Adjust values to account for issues related to rounding 689e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka if (roundedTrueCrop.width() > fullSize.getWidth()) { 690e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka // Adjust the width 691e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka roundedTrueCrop.right = roundedTrueCrop.left + fullSize.getWidth(); 692e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka } 693e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka if (roundedTrueCrop.right > fullSize.getWidth()) { 694e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka // Adjust the left value 695e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka int adjustment = roundedTrueCrop.left - 696e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka Math.max(0, roundedTrueCrop.right - roundedTrueCrop.width()); 697e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka roundedTrueCrop.left -= adjustment; 698e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka roundedTrueCrop.right -= adjustment; 699e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka } 700e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka if (roundedTrueCrop.height() > fullSize.getHeight()) { 701e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka // Adjust the height 702e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka roundedTrueCrop.bottom = roundedTrueCrop.top + fullSize.getHeight(); 703e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka } 704e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka if (roundedTrueCrop.bottom > fullSize.getHeight()) { 705e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka // Adjust the top value 706e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka int adjustment = roundedTrueCrop.top - 707e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka Math.max(0, roundedTrueCrop.bottom - roundedTrueCrop.height()); 708e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka roundedTrueCrop.top -= adjustment; 709e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka roundedTrueCrop.bottom -= adjustment; 710e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka } 711e39c9a953ca11319b747b3aa79f4ccd082b775b7Michael Jurka 712e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, 713e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka roundedTrueCrop.top, roundedTrueCrop.width(), 714e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka roundedTrueCrop.height()); 715e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 716e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 717e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 718e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (crop == null) { 719e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(LOGTAG, "cannot decode file: " + mInUri.toString()); 720e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka failure = true; 721e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return false; 722e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 72369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) { 72469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() }; 72569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka rotateMatrix.mapPoints(dimsAfter); 72669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka dimsAfter[0] = Math.abs(dimsAfter[0]); 72769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka dimsAfter[1] = Math.abs(dimsAfter[1]); 72869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 72969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (!(mOutWidth > 0 && mOutHeight > 0)) { 73069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mOutWidth = Math.round(dimsAfter[0]); 73169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka mOutHeight = Math.round(dimsAfter[1]); 732e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 73369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 73469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]); 735e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight); 73669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 73769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix m = new Matrix(); 73869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka if (mRotation == 0) { 73969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); 74069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } else { 74169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix m1 = new Matrix(); 74269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f); 74369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix m2 = new Matrix(); 74469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka m2.setRotate(mRotation); 74569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix m3 = new Matrix(); 74669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f); 74769784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix m4 = new Matrix(); 74869784065ea78550944b7eddb559c0dac952e20e8Michael Jurka m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); 74969784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 75069784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix c1 = new Matrix(); 75169784065ea78550944b7eddb559c0dac952e20e8Michael Jurka c1.setConcat(m2, m1); 75269784065ea78550944b7eddb559c0dac952e20e8Michael Jurka Matrix c2 = new Matrix(); 75369784065ea78550944b7eddb559c0dac952e20e8Michael Jurka c2.setConcat(m4, m3); 75469784065ea78550944b7eddb559c0dac952e20e8Michael Jurka m.setConcat(c2, c1); 75569784065ea78550944b7eddb559c0dac952e20e8Michael Jurka } 75669784065ea78550944b7eddb559c0dac952e20e8Michael Jurka 757e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), 758e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka (int) returnRect.height(), Bitmap.Config.ARGB_8888); 759e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (tmp != null) { 760e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Canvas c = new Canvas(tmp); 761e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka Paint p = new Paint(); 762e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka p.setFilterBitmap(true); 763e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka c.drawBitmap(crop, m, p); 764e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka crop = tmp; 765e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 766e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 767e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 768e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (mSaveCroppedBitmap) { 769e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka mCroppedBitmap = crop; 770e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 771e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 772e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Get output compression format 773e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka CompressFormat cf = 774e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka convertExtensionToCompressFormat(getFileExtension(mOutputFormat)); 775e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 776e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Compress to byte array 777e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048); 778e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) { 779e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // If we need to set to the wallpaper, set it 780e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka if (mSetWallpaper && wallpaperManager != null) { 781e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka try { 782e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka byte[] outByteArray = tmpOut.toByteArray(); 783e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka wallpaperManager.setStream(new ByteArrayInputStream(outByteArray)); 784e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka if (mOnBitmapCroppedHandler != null) { 785e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka mOnBitmapCroppedHandler.onBitmapCropped(outByteArray); 786e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 787e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } catch (IOException e) { 788e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka Log.w(LOGTAG, "cannot write stream to wallpaper", e); 789e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka failure = true; 790e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 791e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 792e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 793e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka Log.w(LOGTAG, "cannot compress bitmap"); 794e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka failure = true; 795e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 796e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 797e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return !failure; // True if any of the operations failed 798e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 799e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 800e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 801e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected Boolean doInBackground(Void... params) { 802e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return cropBitmap(); 803e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 804e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 805e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka @Override 806e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected void onPostExecute(Boolean result) { 807e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka if (mOnEndRunnable != null) { 808e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka mOnEndRunnable.run(); 809e72aa7f0a64ff55f747751b7972ccb8acebab7daMichael Jurka } 810e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 811e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 812e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 813e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected static RectF getMaxCropRect( 814e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) { 815e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka RectF cropRect = new RectF(); 816e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka // Get a crop rect that will fit this 817e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (inWidth / (float) inHeight > outWidth / (float) outHeight) { 818e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.top = 0; 819e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.bottom = inHeight; 820e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2; 821e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.right = inWidth - cropRect.left; 822e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka if (leftAligned) { 823e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.right -= cropRect.left; 824e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.left = 0; 825e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 826e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } else { 827e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.left = 0; 828e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.right = inWidth; 829e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2; 830e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka cropRect.bottom = inHeight - cropRect.top; 831e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 832e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return cropRect; 833e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 834e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 835e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected static CompressFormat convertExtensionToCompressFormat(String extension) { 836e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG; 837e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 838e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka 839e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka protected static String getFileExtension(String requestFormat) { 840e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka String outputFormat = (requestFormat == null) 841e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ? "jpg" 842e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka : requestFormat; 843e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka outputFormat = outputFormat.toLowerCase(); 844e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka return (outputFormat.equals("png") || outputFormat.equals("gif")) 845e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka ? "png" // We don't support gif compression. 846e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka : "jpg"; 847e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka } 848e8d1bf7a439450b9979701909164a6baffbe8baeMichael Jurka} 849