1104c45677660586026a7e74ef8c47d396403d50eMichael Jurka/* 2104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Copyright (C) 2013 The Android Open Source Project 3104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * 4104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Licensed under the Apache License, Version 2.0 (the "License"); 5104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * you may not use this file except in compliance with the License. 6104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * You may obtain a copy of the License at 7104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * 8104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * http://www.apache.org/licenses/LICENSE-2.0 9104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * 10104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Unless required by applicable law or agreed to in writing, software 11104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * distributed under the License is distributed on an "AS IS" BASIS, 12104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * See the License for the specific language governing permissions and 14104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * limitations under the License. 15104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */ 16104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 17104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapackage com.android.launcher3; 18104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 192d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurkaimport android.app.ActionBar; 20104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.app.Activity; 21104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.app.WallpaperManager; 22104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.content.Context; 232d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurkaimport android.content.Intent; 24104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.content.SharedPreferences; 25adc574ccb1986856b38198ad5a934b7543a1debbMichael Jurkaimport android.content.res.Configuration; 26eadbfc564d84aaf1d800da3d0d6edf6312f89648Michael Jurkaimport android.content.res.Resources; 27104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Bitmap; 28104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Bitmap.CompressFormat; 29104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.BitmapFactory; 30104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.BitmapRegionDecoder; 31104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Canvas; 32104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Matrix; 33104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Paint; 34104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Point; 35104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.Rect; 36104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.graphics.RectF; 37104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.net.Uri; 38104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.os.AsyncTask; 392d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurkaimport android.os.Bundle; 408713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurkaimport android.util.FloatMath; 41104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport android.util.Log; 422d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurkaimport android.view.Display; 432d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurkaimport android.view.View; 442d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurkaimport android.view.WindowManager; 45104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 46104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport com.android.gallery3d.common.Utils; 478713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurkaimport com.android.gallery3d.exif.ExifInterface; 482d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurkaimport com.android.photos.BitmapRegionTileSource; 49104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 50104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.BufferedInputStream; 51104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.ByteArrayInputStream; 52104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.ByteArrayOutputStream; 53104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.FileNotFoundException; 54104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.IOException; 55104c45677660586026a7e74ef8c47d396403d50eMichael Jurkaimport java.io.InputStream; 56104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 57104c45677660586026a7e74ef8c47d396403d50eMichael Jurkapublic class WallpaperCropActivity extends Activity { 58104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private static final String LOGTAG = "Launcher3.CropActivity"; 59104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 60104c45677660586026a7e74ef8c47d396403d50eMichael Jurka protected static final String WALLPAPER_WIDTH_KEY = "wallpaper.width"; 61104c45677660586026a7e74ef8c47d396403d50eMichael Jurka protected static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height"; 62104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private static final int DEFAULT_COMPRESS_QUALITY = 90; 63104c45677660586026a7e74ef8c47d396403d50eMichael Jurka /** 64104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * The maximum bitmap size we allow to be returned through the intent. 65104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * Intents have a maximum of 1MB in total size. However, the Bitmap seems to 66104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * have some overhead to hit so that we go way below the limit here to make 67104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * sure the intent stays below 1MB.We should consider just returning a byte 68104c45677660586026a7e74ef8c47d396403d50eMichael Jurka * array instead of a Bitmap instance to avoid overhead. 69104c45677660586026a7e74ef8c47d396403d50eMichael Jurka */ 70104c45677660586026a7e74ef8c47d396403d50eMichael Jurka public static final int MAX_BMAP_IN_INTENT = 750000; 712d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka private static final float WALLPAPER_SCREENS_SPAN = 2f; 72104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 732d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka protected CropView mCropView; 742d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka protected Uri mUri; 752d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 762d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka @Override 772d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka protected void onCreate(Bundle savedInstanceState) { 782d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka super.onCreate(savedInstanceState); 792d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka init(); 807267fa58693549894999db17359ce26f8bc7b4a7Michael Jurka if (!enableRotation()) { 817267fa58693549894999db17359ce26f8bc7b4a7Michael Jurka setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT); 827267fa58693549894999db17359ce26f8bc7b4a7Michael Jurka } 832d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 842d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 852d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka protected void init() { 862d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka setContentView(R.layout.wallpaper_cropper); 872d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 882d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka mCropView = (CropView) findViewById(R.id.cropView); 892d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 90f6502c316c602e44fd5a1706013d7123a9eb1022Michael Jurka Intent cropIntent = getIntent(); 912d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka final Uri imageUri = cropIntent.getData(); 922d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 93f6502c316c602e44fd5a1706013d7123a9eb1022Michael Jurka if (imageUri == null) { 94f6502c316c602e44fd5a1706013d7123a9eb1022Michael Jurka Log.e(LOGTAG, "No URI passed in intent, exiting WallpaperCropActivity"); 95f6502c316c602e44fd5a1706013d7123a9eb1022Michael Jurka finish(); 96f6502c316c602e44fd5a1706013d7123a9eb1022Michael Jurka return; 97f6502c316c602e44fd5a1706013d7123a9eb1022Michael Jurka } 98f6502c316c602e44fd5a1706013d7123a9eb1022Michael Jurka 998713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka int rotation = getRotationFromExif(this, imageUri); 1008713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka mCropView.setTileSource(new BitmapRegionTileSource(this, imageUri, 1024, rotation), null); 1012d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka mCropView.setTouchEnabled(true); 1022d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // Action bar 1032d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // Show the custom action bar view 1042d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka final ActionBar actionBar = getActionBar(); 1052d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka actionBar.setCustomView(R.layout.actionbar_set_wallpaper); 1062d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka actionBar.getCustomView().setOnClickListener( 1072d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka new View.OnClickListener() { 1082d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka @Override 1092d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka public void onClick(View v) { 1102d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka boolean finishActivityWhenDone = true; 111be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone); 1122d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 1132d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka }); 1147267fa58693549894999db17359ce26f8bc7b4a7Michael Jurka } 1157267fa58693549894999db17359ce26f8bc7b4a7Michael Jurka 1167267fa58693549894999db17359ce26f8bc7b4a7Michael Jurka public boolean enableRotation() { 1177267fa58693549894999db17359ce26f8bc7b4a7Michael Jurka return getResources().getBoolean(R.bool.allow_rotation); 1182d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 1192d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 120adc574ccb1986856b38198ad5a934b7543a1debbMichael Jurka public static String getSharedPreferencesKey() { 121adc574ccb1986856b38198ad5a934b7543a1debbMichael Jurka return WallpaperCropActivity.class.getName(); 122adc574ccb1986856b38198ad5a934b7543a1debbMichael Jurka } 123adc574ccb1986856b38198ad5a934b7543a1debbMichael Jurka 1242d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // As a ratio of screen height, the total distance we want the parallax effect to span 1252d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // horizontally 1262d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka private static float wallpaperTravelToScreenWidthRatio(int width, int height) { 1272d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka float aspectRatio = width / (float) height; 1282d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 1292d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width 1302d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width 1312d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // We will use these two data points to extrapolate how much the wallpaper parallax effect 1322d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // to span (ie travel) at any aspect ratio: 1332d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 1342d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka final float ASPECT_RATIO_LANDSCAPE = 16/10f; 1352d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka final float ASPECT_RATIO_PORTRAIT = 10/16f; 1362d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f; 1372d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f; 1382d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 1392d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // To find out the desired width at different aspect ratios, we use the following two 1402d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // formulas, where the coefficient on x is the aspect ratio (width/height): 1412d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // (16/10)x + y = 1.5 1422d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // (10/16)x + y = 1.2 1432d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // We solve for x and y and end up with a final formula: 1442d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka final float x = 1452d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) / 1462d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT); 1472d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT; 1482d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka return x * aspectRatio + y; 1492d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 1502d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 1512d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka static protected Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) { 1522d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka Point minDims = new Point(); 1532d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka Point maxDims = new Point(); 1542d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims); 1552d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 1562d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka int maxDim = Math.max(maxDims.x, maxDims.y); 15788f33511b8f84521aeb285114b70ccb938d1bf81Michael Jurka int minDim = Math.max(minDims.x, minDims.y); 1582d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 1592d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { 1602d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka Point realSize = new Point(); 1612d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka windowManager.getDefaultDisplay().getRealSize(realSize); 1622d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka maxDim = Math.max(realSize.x, realSize.y); 16388f33511b8f84521aeb285114b70ccb938d1bf81Michael Jurka minDim = Math.min(realSize.x, realSize.y); 1642d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 1652d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 1662d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // We need to ensure that there is enough extra space in the wallpaper 1672d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // for the intended 1682d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // parallax effects 1692d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka final int defaultWidth, defaultHeight; 170adc574ccb1986856b38198ad5a934b7543a1debbMichael Jurka if (isScreenLarge(res)) { 1712d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim)); 1722d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka defaultHeight = maxDim; 1732d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } else { 1742d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim); 1752d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka defaultHeight = maxDim; 1762d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 1772d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka return new Point(defaultWidth, defaultHeight); 1782d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 1792d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 1808713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka public static int getRotationFromExif(String path) { 1818713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka return getRotationFromExifHelper(path, null, 0, null, null); 1828713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka } 1838713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 1848713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka public static int getRotationFromExif(Context context, Uri uri) { 1858713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka return getRotationFromExifHelper(null, null, 0, context, uri); 1868713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka } 1878713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 1888713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka public static int getRotationFromExif(Resources res, int resId) { 1898713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka return getRotationFromExifHelper(null, res, resId, null, null); 1908713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka } 1918713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 1928713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka private static int getRotationFromExifHelper( 1938713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka String path, Resources res, int resId, Context context, Uri uri) { 1948713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka ExifInterface ei = new ExifInterface(); 1958713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka try { 1968713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka if (path != null) { 1978713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka ei.readExif(path); 1988713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka } else if (uri != null) { 1998713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka InputStream is = context.getContentResolver().openInputStream(uri); 2008713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka BufferedInputStream bis = new BufferedInputStream(is); 2018713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka ei.readExif(bis); 2028713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka } else { 2038713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka InputStream is = res.openRawResource(resId); 2048713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka BufferedInputStream bis = new BufferedInputStream(is); 2058713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka ei.readExif(bis); 2068713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka } 2078713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION); 2088713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka if (ori != null) { 2098713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka return ExifInterface.getRotationForOrientationValue(ori.shortValue()); 2108713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka } 2118713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka } catch (IOException e) { 2128713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Log.w(LOGTAG, "Getting exif data failed", e); 2138713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka } 2148713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka return 0; 2158713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka } 2168713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 217be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka protected void setWallpaper(String filePath, final boolean finishActivityWhenDone) { 2188713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka int rotation = getRotationFromExif(filePath); 2198713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka BitmapCropTask cropTask = new BitmapCropTask( 2208713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka this, filePath, null, rotation, 0, 0, true, false, null); 221be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka final Point bounds = cropTask.getImageBounds(); 222be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka Runnable onEndCrop = new Runnable() { 223be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka public void run() { 224be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka updateWallpaperDimensions(bounds.x, bounds.y); 225be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka if (finishActivityWhenDone) { 226be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka setResult(Activity.RESULT_OK); 227be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka finish(); 228be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } 229be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } 230be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka }; 231be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka cropTask.setOnEndRunnable(onEndCrop); 232be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka cropTask.setNoCrop(true); 233be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka cropTask.execute(); 234be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } 235be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka 236be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka protected void cropImageAndSetWallpaper( 237be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka Resources res, int resId, final boolean finishActivityWhenDone) { 2382d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // crop this image and scale it down to the default wallpaper size for 2392d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // this device 2408713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka int rotation = getRotationFromExif(res, resId); 2412d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka Point inSize = mCropView.getSourceDimensions(); 2422d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka Point outSize = getDefaultWallpaperSize(getResources(), 2432d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka getWindowManager()); 2442d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka RectF crop = getMaxCropRect( 245be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka inSize.x, inSize.y, outSize.x, outSize.y, false); 2462d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka Runnable onEndCrop = new Runnable() { 2472d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka public void run() { 2482d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // Passing 0, 0 will cause launcher to revert to using the 2492d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // default wallpaper size 2502d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka updateWallpaperDimensions(0, 0); 2512d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka if (finishActivityWhenDone) { 2522d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka setResult(Activity.RESULT_OK); 2532d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka finish(); 2542d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 2552d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 2562d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka }; 2571e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka BitmapCropTask cropTask = new BitmapCropTask(this, res, resId, 2588713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka crop, rotation, outSize.x, outSize.y, true, false, onEndCrop); 2592d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka cropTask.execute(); 2602d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 2612d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 262adc574ccb1986856b38198ad5a934b7543a1debbMichael Jurka private static boolean isScreenLarge(Resources res) { 263adc574ccb1986856b38198ad5a934b7543a1debbMichael Jurka Configuration config = res.getConfiguration(); 264adc574ccb1986856b38198ad5a934b7543a1debbMichael Jurka return config.smallestScreenWidthDp >= 720; 265adc574ccb1986856b38198ad5a934b7543a1debbMichael Jurka } 266adc574ccb1986856b38198ad5a934b7543a1debbMichael Jurka 267be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka protected void cropImageAndSetWallpaper(Uri uri, 268be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) { 269fe0ace3583bc57adbaaec730b1b1757032e90c41Michael Jurka // Get the crop 270fe0ace3583bc57adbaaec730b1b1757032e90c41Michael Jurka boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; 271fe0ace3583bc57adbaaec730b1b1757032e90c41Michael Jurka 2722d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka Point minDims = new Point(); 2732d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka Point maxDims = new Point(); 2742d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka Display d = getWindowManager().getDefaultDisplay(); 2752d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka d.getCurrentSizeRange(minDims, maxDims); 2762d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 2772d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka Point displaySize = new Point(); 2782d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka d.getSize(displaySize); 2792d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 2802d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka int maxDim = Math.max(maxDims.x, maxDims.y); 2812d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka final int minDim = Math.min(minDims.x, minDims.y); 282fe0ace3583bc57adbaaec730b1b1757032e90c41Michael Jurka int defaultWallpaperWidth; 283adc574ccb1986856b38198ad5a934b7543a1debbMichael Jurka if (isScreenLarge(getResources())) { 284fe0ace3583bc57adbaaec730b1b1757032e90c41Michael Jurka defaultWallpaperWidth = (int) (maxDim * 2852d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka wallpaperTravelToScreenWidthRatio(maxDim, minDim)); 2862d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } else { 287fe0ace3583bc57adbaaec730b1b1757032e90c41Michael Jurka defaultWallpaperWidth = Math.max((int) 2882d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka (minDim * WALLPAPER_SCREENS_SPAN), maxDim); 2892d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 2902d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 2912d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka boolean isPortrait = displaySize.x < displaySize.y; 2922d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka int portraitHeight; 2932d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka if (isPortrait) { 2942d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka portraitHeight = mCropView.getHeight(); 2952d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } else { 2962d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // TODO: how to actually get the proper portrait height? 2972d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // This is not quite right: 2982d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka portraitHeight = Math.max(maxDims.x, maxDims.y); 2992d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 3002d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka if (android.os.Build.VERSION.SDK_INT >= 3012d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { 3022d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka Point realSize = new Point(); 3032d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka d.getRealSize(realSize); 3042d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka portraitHeight = Math.max(realSize.x, realSize.y); 3052d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 3062d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // Get the crop 3072d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka RectF cropRect = mCropView.getCrop(); 3088713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka int cropRotation = mCropView.getImageRotation(); 3092d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka float cropScale = mCropView.getWidth() / (float) cropRect.width(); 3102d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 3118713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Point inSize = mCropView.getSourceDimensions(); 3128713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Matrix rotateMatrix = new Matrix(); 3138713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka rotateMatrix.setRotate(cropRotation); 3148713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka float[] rotatedInSize = new float[] { inSize.x, inSize.y }; 3158713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka rotateMatrix.mapPoints(rotatedInSize); 3168713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka rotatedInSize[0] = Math.abs(rotatedInSize[0]); 3178713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka rotatedInSize[1] = Math.abs(rotatedInSize[1]); 3188713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 3192d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // ADJUST CROP WIDTH 3202d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // Extend the crop all the way to the right, for parallax 321fe0ace3583bc57adbaaec730b1b1757032e90c41Michael Jurka // (or all the way to the left, in RTL) 3228713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka float extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left; 3232d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // Cap the amount of extra width 324fe0ace3583bc57adbaaec730b1b1757032e90c41Michael Jurka float maxExtraSpace = defaultWallpaperWidth / cropScale - cropRect.width(); 325fe0ace3583bc57adbaaec730b1b1757032e90c41Michael Jurka extraSpace = Math.min(extraSpace, maxExtraSpace); 3262d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 327fe0ace3583bc57adbaaec730b1b1757032e90c41Michael Jurka if (ltr) { 328fe0ace3583bc57adbaaec730b1b1757032e90c41Michael Jurka cropRect.right += extraSpace; 329fe0ace3583bc57adbaaec730b1b1757032e90c41Michael Jurka } else { 330fe0ace3583bc57adbaaec730b1b1757032e90c41Michael Jurka cropRect.left -= extraSpace; 331fe0ace3583bc57adbaaec730b1b1757032e90c41Michael Jurka } 3322d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 3332d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // ADJUST CROP HEIGHT 3342d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka if (isPortrait) { 3352d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka cropRect.bottom = cropRect.top + portraitHeight / cropScale; 3362d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } else { // LANDSCAPE 3372d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka float extraPortraitHeight = 3382d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka portraitHeight / cropScale - cropRect.height(); 3392d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka float expandHeight = 3408713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top), 3412d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka extraPortraitHeight / 2); 3422d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka cropRect.top -= expandHeight; 3432d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka cropRect.bottom += expandHeight; 3442d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 3452d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka final int outWidth = (int) Math.round(cropRect.width() * cropScale); 3462d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka final int outHeight = (int) Math.round(cropRect.height() * cropScale); 3472d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 3482d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka Runnable onEndCrop = new Runnable() { 3492d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka public void run() { 3502d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka updateWallpaperDimensions(outWidth, outHeight); 3512d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka if (finishActivityWhenDone) { 3522d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka setResult(Activity.RESULT_OK); 3532d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka finish(); 3542d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 3552d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 3562d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka }; 3571e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka BitmapCropTask cropTask = new BitmapCropTask(this, uri, 3588713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop); 359be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka if (onBitmapCroppedHandler != null) { 360be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka cropTask.setOnBitmapCropped(onBitmapCroppedHandler); 361be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } 3622d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka cropTask.execute(); 3632d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 364104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 365be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka public interface OnBitmapCroppedHandler { 366be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka public void onBitmapCropped(byte[] imageBytes); 367be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } 368be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka 3691e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka protected static class BitmapCropTask extends AsyncTask<Void, Void, Boolean> { 370104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Uri mInUri = null; 371be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka Context mContext; 372be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka String mInFilePath; 373be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka byte[] mInImageBytes; 374fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka int mInResId = 0; 375104c45677660586026a7e74ef8c47d396403d50eMichael Jurka InputStream mInStream; 376104c45677660586026a7e74ef8c47d396403d50eMichael Jurka RectF mCropBounds = null; 377104c45677660586026a7e74ef8c47d396403d50eMichael Jurka int mOutWidth, mOutHeight; 3788713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka int mRotation; 379104c45677660586026a7e74ef8c47d396403d50eMichael Jurka String mOutputFormat = "jpg"; // for now 380104c45677660586026a7e74ef8c47d396403d50eMichael Jurka boolean mSetWallpaper; 381104c45677660586026a7e74ef8c47d396403d50eMichael Jurka boolean mSaveCroppedBitmap; 382104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Bitmap mCroppedBitmap; 383fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka Runnable mOnEndRunnable; 384eadbfc564d84aaf1d800da3d0d6edf6312f89648Michael Jurka Resources mResources; 385be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka OnBitmapCroppedHandler mOnBitmapCroppedHandler; 386be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka boolean mNoCrop; 387be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka 388be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka public BitmapCropTask(Context c, String filePath, 3898713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka RectF cropBounds, int rotation, int outWidth, int outHeight, 390be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 391be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka mContext = c; 392be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka mInFilePath = filePath; 3938713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka init(cropBounds, rotation, 3948713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); 395be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } 396be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka 397be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka public BitmapCropTask(byte[] imageBytes, 3988713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka RectF cropBounds, int rotation, int outWidth, int outHeight, 399be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 400be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka mInImageBytes = imageBytes; 4018713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka init(cropBounds, rotation, 4028713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); 403be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } 404104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 4051e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka public BitmapCropTask(Context c, Uri inUri, 4068713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka RectF cropBounds, int rotation, int outWidth, int outHeight, 407fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 4081e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka mContext = c; 409104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mInUri = inUri; 4108713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka init(cropBounds, rotation, 4118713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); 412fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka } 413fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka 4141e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka public BitmapCropTask(Context c, Resources res, int inResId, 4158713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka RectF cropBounds, int rotation, int outWidth, int outHeight, 416fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 4171e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka mContext = c; 418fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka mInResId = inResId; 419be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka mResources = res; 4208713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka init(cropBounds, rotation, 4218713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable); 422be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } 423be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka 4248713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka private void init(RectF cropBounds, int rotation, int outWidth, int outHeight, 425be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) { 426fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka mCropBounds = cropBounds; 4278713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka mRotation = rotation; 428fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka mOutWidth = outWidth; 429fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka mOutHeight = outHeight; 430fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka mSetWallpaper = setWallpaper; 431fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka mSaveCroppedBitmap = saveCroppedBitmap; 432fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka mOnEndRunnable = onEndRunnable; 433be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } 434be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka 435be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka public void setOnBitmapCropped(OnBitmapCroppedHandler handler) { 436be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka mOnBitmapCroppedHandler = handler; 437be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } 438be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka 439be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka public void setNoCrop(boolean value) { 440be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka mNoCrop = value; 441be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } 442be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka 443be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka public void setOnEndRunnable(Runnable onEndRunnable) { 444be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka mOnEndRunnable = onEndRunnable; 445104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 446104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 447104c45677660586026a7e74ef8c47d396403d50eMichael Jurka // Helper to setup input stream 448104c45677660586026a7e74ef8c47d396403d50eMichael Jurka private void regenerateInputStream() { 449be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) { 450be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " + 451be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka "image byte array given"); 452104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } else { 453104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Utils.closeSilently(mInStream); 454104c45677660586026a7e74ef8c47d396403d50eMichael Jurka try { 455fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka if (mInUri != null) { 456fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka mInStream = new BufferedInputStream( 4571e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka mContext.getContentResolver().openInputStream(mInUri)); 458be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } else if (mInFilePath != null) { 459be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka mInStream = mContext.openFileInput(mInFilePath); 460be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } else if (mInImageBytes != null) { 461be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka mInStream = new BufferedInputStream( 462be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka new ByteArrayInputStream(mInImageBytes)); 463fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka } else { 464fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka mInStream = new BufferedInputStream( 465eadbfc564d84aaf1d800da3d0d6edf6312f89648Michael Jurka mResources.openRawResource(mInResId)); 466fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka } 467104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } catch (FileNotFoundException e) { 468104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e); 469104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 470104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 471104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 472104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 473104c45677660586026a7e74ef8c47d396403d50eMichael Jurka public Point getImageBounds() { 474104c45677660586026a7e74ef8c47d396403d50eMichael Jurka regenerateInputStream(); 475104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (mInStream != null) { 476104c45677660586026a7e74ef8c47d396403d50eMichael Jurka BitmapFactory.Options options = new BitmapFactory.Options(); 477104c45677660586026a7e74ef8c47d396403d50eMichael Jurka options.inJustDecodeBounds = true; 478104c45677660586026a7e74ef8c47d396403d50eMichael Jurka BitmapFactory.decodeStream(mInStream, null, options); 479104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (options.outWidth != 0 && options.outHeight != 0) { 480104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return new Point(options.outWidth, options.outHeight); 481104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 482104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 483104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return null; 484104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 485104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 486104c45677660586026a7e74ef8c47d396403d50eMichael Jurka public void setCropBounds(RectF cropBounds) { 487104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mCropBounds = cropBounds; 488104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 489104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 490104c45677660586026a7e74ef8c47d396403d50eMichael Jurka public Bitmap getCroppedBitmap() { 491104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return mCroppedBitmap; 492104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 493104c45677660586026a7e74ef8c47d396403d50eMichael Jurka public boolean cropBitmap() { 494104c45677660586026a7e74ef8c47d396403d50eMichael Jurka boolean failure = false; 495104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 496104c45677660586026a7e74ef8c47d396403d50eMichael Jurka regenerateInputStream(); 497104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 4981e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka WallpaperManager wallpaperManager = null; 4991e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka if (mSetWallpaper) { 5001e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext()); 5011e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka } 5021e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka if (mSetWallpaper && mNoCrop && mInStream != null) { 503be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka try { 5041e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka wallpaperManager.setStream(mInStream); 505be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } catch (IOException e) { 506be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka Log.w(LOGTAG, "cannot write stream to wallpaper", e); 507be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka failure = true; 508be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } 509be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka return !failure; 510be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } 511104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (mInStream != null) { 512104c45677660586026a7e74ef8c47d396403d50eMichael Jurka // Find crop bounds (scaled to original image size) 513104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Rect roundedTrueCrop = new Rect(); 5148713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Matrix rotateMatrix = new Matrix(); 5158713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Matrix inverseRotateMatrix = new Matrix(); 5168713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka if (mRotation > 0) { 5178713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka rotateMatrix.setRotate(mRotation); 5188713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka inverseRotateMatrix.setRotate(-mRotation); 5198713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 5208713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka mCropBounds.roundOut(roundedTrueCrop); 5218713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka mCropBounds = new RectF(roundedTrueCrop); 5228713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 5238713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Point bounds = getImageBounds(); 5248713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 5258713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka float[] rotatedBounds = new float[] { bounds.x, bounds.y }; 5268713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka rotateMatrix.mapPoints(rotatedBounds); 5278713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka rotatedBounds[0] = Math.abs(rotatedBounds[0]); 5288713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka rotatedBounds[1] = Math.abs(rotatedBounds[1]); 5298713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 5308713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2); 5318713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka inverseRotateMatrix.mapRect(mCropBounds); 5328713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka mCropBounds.offset(bounds.x/2, bounds.y/2); 5338713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 5348713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka regenerateInputStream(); 5358713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka } 5368713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 537104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mCropBounds.roundOut(roundedTrueCrop); 538104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 539104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) { 540104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Log.w(LOGTAG, "crop has bad values for full size image"); 541104c45677660586026a7e74ef8c47d396403d50eMichael Jurka failure = true; 542104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return false; 543104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 544104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 545104c45677660586026a7e74ef8c47d396403d50eMichael Jurka // See how much we're reducing the size of the image 546104c45677660586026a7e74ef8c47d396403d50eMichael Jurka int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / mOutWidth, 547104c45677660586026a7e74ef8c47d396403d50eMichael Jurka roundedTrueCrop.height() / mOutHeight); 548104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 549104c45677660586026a7e74ef8c47d396403d50eMichael Jurka // Attempt to open a region decoder 550104c45677660586026a7e74ef8c47d396403d50eMichael Jurka BitmapRegionDecoder decoder = null; 551104c45677660586026a7e74ef8c47d396403d50eMichael Jurka try { 552104c45677660586026a7e74ef8c47d396403d50eMichael Jurka decoder = BitmapRegionDecoder.newInstance(mInStream, true); 553104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } catch (IOException e) { 554104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e); 555104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 556104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 557104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Bitmap crop = null; 558104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (decoder != null) { 559104c45677660586026a7e74ef8c47d396403d50eMichael Jurka // Do region decoding to get crop bitmap 560104c45677660586026a7e74ef8c47d396403d50eMichael Jurka BitmapFactory.Options options = new BitmapFactory.Options(); 561104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (scaleDownSampleSize > 1) { 562104c45677660586026a7e74ef8c47d396403d50eMichael Jurka options.inSampleSize = scaleDownSampleSize; 563104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 564104c45677660586026a7e74ef8c47d396403d50eMichael Jurka crop = decoder.decodeRegion(roundedTrueCrop, options); 565104c45677660586026a7e74ef8c47d396403d50eMichael Jurka decoder.recycle(); 566104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 567104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 568104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (crop == null) { 569104c45677660586026a7e74ef8c47d396403d50eMichael Jurka // BitmapRegionDecoder has failed, try to crop in-memory 570104c45677660586026a7e74ef8c47d396403d50eMichael Jurka regenerateInputStream(); 571104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Bitmap fullSize = null; 572104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (mInStream != null) { 573104c45677660586026a7e74ef8c47d396403d50eMichael Jurka BitmapFactory.Options options = new BitmapFactory.Options(); 574104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (scaleDownSampleSize > 1) { 575104c45677660586026a7e74ef8c47d396403d50eMichael Jurka options.inSampleSize = scaleDownSampleSize; 576104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 577104c45677660586026a7e74ef8c47d396403d50eMichael Jurka fullSize = BitmapFactory.decodeStream(mInStream, null, options); 578104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 579104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (fullSize != null) { 5808713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka mCropBounds.left /= scaleDownSampleSize; 5818713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka mCropBounds.top /= scaleDownSampleSize; 5828713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka mCropBounds.bottom /= scaleDownSampleSize; 5838713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka mCropBounds.right /= scaleDownSampleSize; 5848713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka mCropBounds.roundOut(roundedTrueCrop); 5858713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 586104c45677660586026a7e74ef8c47d396403d50eMichael Jurka crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left, 587104c45677660586026a7e74ef8c47d396403d50eMichael Jurka roundedTrueCrop.top, roundedTrueCrop.width(), 588104c45677660586026a7e74ef8c47d396403d50eMichael Jurka roundedTrueCrop.height()); 589104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 590104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 591104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 592104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (crop == null) { 593104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Log.w(LOGTAG, "cannot decode file: " + mInUri.toString()); 594104c45677660586026a7e74ef8c47d396403d50eMichael Jurka failure = true; 595104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return false; 596104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 5978713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) { 5988713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() }; 5998713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka rotateMatrix.mapPoints(dimsAfter); 6008713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka dimsAfter[0] = Math.abs(dimsAfter[0]); 6018713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka dimsAfter[1] = Math.abs(dimsAfter[1]); 6028713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 6038713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka if (!(mOutWidth > 0 && mOutHeight > 0)) { 6048713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka mOutWidth = Math.round(dimsAfter[0]); 6058713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka mOutHeight = Math.round(dimsAfter[1]); 606104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 6078713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 6088713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]); 609fd471c0b4f028948d5aaefe37d4b9582b4b4372fMichael Jurka RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight); 6108713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 6118713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Matrix m = new Matrix(); 6128713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka if (mRotation == 0) { 6138713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); 6148713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka } else { 6158713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Matrix m1 = new Matrix(); 6168713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f); 6178713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Matrix m2 = new Matrix(); 6188713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka m2.setRotate(mRotation); 6198713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Matrix m3 = new Matrix(); 6208713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f); 6218713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Matrix m4 = new Matrix(); 6228713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL); 6238713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 6248713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Matrix c1 = new Matrix(); 6258713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka c1.setConcat(m2, m1); 6268713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka Matrix c2 = new Matrix(); 6278713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka c2.setConcat(m4, m3); 6288713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka m.setConcat(c2, c1); 6298713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka } 6308713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka 631104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(), 632104c45677660586026a7e74ef8c47d396403d50eMichael Jurka (int) returnRect.height(), Bitmap.Config.ARGB_8888); 633104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (tmp != null) { 634104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Canvas c = new Canvas(tmp); 63516205da30e223368b31c68b7d76ac3d52f940c88Michael Jurka Paint p = new Paint(); 63616205da30e223368b31c68b7d76ac3d52f940c88Michael Jurka p.setFilterBitmap(true); 63716205da30e223368b31c68b7d76ac3d52f940c88Michael Jurka c.drawBitmap(crop, m, p); 638104c45677660586026a7e74ef8c47d396403d50eMichael Jurka crop = tmp; 639104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 640104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 641104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 642104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (mSaveCroppedBitmap) { 643104c45677660586026a7e74ef8c47d396403d50eMichael Jurka mCroppedBitmap = crop; 644104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 645104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 646104c45677660586026a7e74ef8c47d396403d50eMichael Jurka // Get output compression format 647104c45677660586026a7e74ef8c47d396403d50eMichael Jurka CompressFormat cf = 648104c45677660586026a7e74ef8c47d396403d50eMichael Jurka convertExtensionToCompressFormat(getFileExtension(mOutputFormat)); 649104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 650104c45677660586026a7e74ef8c47d396403d50eMichael Jurka // Compress to byte array 651104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048); 652104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) { 653104c45677660586026a7e74ef8c47d396403d50eMichael Jurka // If we need to set to the wallpaper, set it 6541e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka if (mSetWallpaper && wallpaperManager != null) { 6551e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka try { 6561e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka byte[] outByteArray = tmpOut.toByteArray(); 6571e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka wallpaperManager.setStream(new ByteArrayInputStream(outByteArray)); 6581e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka if (mOnBitmapCroppedHandler != null) { 6591e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka mOnBitmapCroppedHandler.onBitmapCropped(outByteArray); 660104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 6611e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka } catch (IOException e) { 6621e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka Log.w(LOGTAG, "cannot write stream to wallpaper", e); 6631e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka failure = true; 664104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 665104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 666104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } else { 667104c45677660586026a7e74ef8c47d396403d50eMichael Jurka Log.w(LOGTAG, "cannot compress bitmap"); 668104c45677660586026a7e74ef8c47d396403d50eMichael Jurka failure = true; 669104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 670104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 671104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return !failure; // True if any of the operations failed 672104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 673104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 674104c45677660586026a7e74ef8c47d396403d50eMichael Jurka @Override 675104c45677660586026a7e74ef8c47d396403d50eMichael Jurka protected Boolean doInBackground(Void... params) { 676104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return cropBitmap(); 677104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 678104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 679104c45677660586026a7e74ef8c47d396403d50eMichael Jurka @Override 680104c45677660586026a7e74ef8c47d396403d50eMichael Jurka protected void onPostExecute(Boolean result) { 6811e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka if (mOnEndRunnable != null) { 6821e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka mOnEndRunnable.run(); 6831e4e6ddb38d0b867d0564be834f3425ed53d46e1Michael Jurka } 684104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 685104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 686104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 687104c45677660586026a7e74ef8c47d396403d50eMichael Jurka protected void updateWallpaperDimensions(int width, int height) { 688adc574ccb1986856b38198ad5a934b7543a1debbMichael Jurka String spKey = getSharedPreferencesKey(); 689104c45677660586026a7e74ef8c47d396403d50eMichael Jurka SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE); 690104c45677660586026a7e74ef8c47d396403d50eMichael Jurka SharedPreferences.Editor editor = sp.edit(); 691104c45677660586026a7e74ef8c47d396403d50eMichael Jurka if (width != 0 && height != 0) { 692104c45677660586026a7e74ef8c47d396403d50eMichael Jurka editor.putInt(WALLPAPER_WIDTH_KEY, width); 693104c45677660586026a7e74ef8c47d396403d50eMichael Jurka editor.putInt(WALLPAPER_HEIGHT_KEY, height); 694104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } else { 695104c45677660586026a7e74ef8c47d396403d50eMichael Jurka editor.remove(WALLPAPER_WIDTH_KEY); 696104c45677660586026a7e74ef8c47d396403d50eMichael Jurka editor.remove(WALLPAPER_HEIGHT_KEY); 697104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 698104c45677660586026a7e74ef8c47d396403d50eMichael Jurka editor.commit(); 699fe09cb7fe3ec1ab3e7aabd2d0415de61df31c4dbMichael Jurka 7002d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka suggestWallpaperDimension(getResources(), 701104c45677660586026a7e74ef8c47d396403d50eMichael Jurka sp, getWindowManager(), WallpaperManager.getInstance(this)); 702104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 703104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 7042d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka static public void suggestWallpaperDimension(Resources res, 7052d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka final SharedPreferences sharedPrefs, 7062d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka WindowManager windowManager, 7072d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka final WallpaperManager wallpaperManager) { 7088713a94562c535bd2cc7d72e767caf9468d7bbaaMichael Jurka final Point defaultWallpaperSize = getDefaultWallpaperSize(res, windowManager); 7092d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 7102d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka new Thread("suggestWallpaperDimension") { 7112d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka public void run() { 7122d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // If we have saved a wallpaper width/height, use that instead 7132d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, defaultWallpaperSize.x); 7142d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, defaultWallpaperSize.y); 7152d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight); 7162d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 7172d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka }.start(); 7182d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 7192d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 720be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka protected static RectF getMaxCropRect( 721be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) { 7222d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka RectF cropRect = new RectF(); 7232d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka // Get a crop rect that will fit this 7242d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka if (inWidth / (float) inHeight > outWidth / (float) outHeight) { 7252d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka cropRect.top = 0; 7262d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka cropRect.bottom = inHeight; 7272d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2; 7282d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka cropRect.right = inWidth - cropRect.left; 729be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka if (leftAligned) { 730be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka cropRect.right -= cropRect.left; 731be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka cropRect.left = 0; 732be2f8ddd8067d0da71e2d03e42bcad17f9430439Michael Jurka } 7332d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } else { 7342d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka cropRect.left = 0; 7352d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka cropRect.right = inWidth; 7362d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2; 7372d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka cropRect.bottom = inHeight - cropRect.top; 7382d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 7392d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka return cropRect; 7402d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka } 7412d8de5845be5f49d3767b8354f0d5479a0715abeMichael Jurka 742104c45677660586026a7e74ef8c47d396403d50eMichael Jurka protected static CompressFormat convertExtensionToCompressFormat(String extension) { 743104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG; 744104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 745104c45677660586026a7e74ef8c47d396403d50eMichael Jurka 746104c45677660586026a7e74ef8c47d396403d50eMichael Jurka protected static String getFileExtension(String requestFormat) { 747104c45677660586026a7e74ef8c47d396403d50eMichael Jurka String outputFormat = (requestFormat == null) 748104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ? "jpg" 749104c45677660586026a7e74ef8c47d396403d50eMichael Jurka : requestFormat; 750104c45677660586026a7e74ef8c47d396403d50eMichael Jurka outputFormat = outputFormat.toLowerCase(); 751104c45677660586026a7e74ef8c47d396403d50eMichael Jurka return (outputFormat.equals("png") || outputFormat.equals("gif")) 752104c45677660586026a7e74ef8c47d396403d50eMichael Jurka ? "png" // We don't support gif compression. 753104c45677660586026a7e74ef8c47d396403d50eMichael Jurka : "jpg"; 754104c45677660586026a7e74ef8c47d396403d50eMichael Jurka } 755104c45677660586026a7e74ef8c47d396403d50eMichael Jurka} 756