Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2013 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package com.android.launcher3; |
| 18 | |
Sunny Goyal | 06a4d3f | 2015-02-12 11:19:39 -0800 | [diff] [blame] | 19 | import android.annotation.TargetApi; |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 20 | import android.app.ActionBar; |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 21 | import android.app.Activity; |
| 22 | import android.app.WallpaperManager; |
| 23 | import android.content.Context; |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 24 | import android.content.Intent; |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 25 | import android.content.SharedPreferences; |
Michael Jurka | adc574c | 2013-09-12 00:05:02 +0200 | [diff] [blame] | 26 | import android.content.res.Configuration; |
Michael Jurka | eadbfc5 | 2013-09-04 00:45:37 +0200 | [diff] [blame] | 27 | import android.content.res.Resources; |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 28 | import android.graphics.Bitmap; |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 29 | import android.graphics.Matrix; |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 30 | import android.graphics.Point; |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 31 | import android.graphics.RectF; |
| 32 | import android.net.Uri; |
Sunny Goyal | 06a4d3f | 2015-02-12 11:19:39 -0800 | [diff] [blame] | 33 | import android.os.Build; |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 34 | import android.os.Bundle; |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 35 | import android.os.Handler; |
| 36 | import android.os.HandlerThread; |
| 37 | import android.os.Message; |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 38 | import android.util.Log; |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 39 | import android.view.Display; |
| 40 | import android.view.View; |
Michael Jurka | 619a180 | 2013-10-29 15:50:06 +0100 | [diff] [blame] | 41 | import android.widget.Toast; |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 42 | |
Sunny Goyal | 06a4d3f | 2015-02-12 11:19:39 -0800 | [diff] [blame] | 43 | import com.android.gallery3d.common.BitmapCropTask; |
| 44 | import com.android.gallery3d.common.BitmapUtils; |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 45 | import com.android.gallery3d.common.Utils; |
Sunny Goyal | 1d0b093 | 2015-03-20 18:51:38 -0700 | [diff] [blame^] | 46 | import com.android.launcher3.base.BaseActivity; |
Adam Cohen | 091440a | 2015-03-18 14:16:05 -0700 | [diff] [blame] | 47 | import com.android.launcher3.util.Thunk; |
Sunny Goyal | 6a1e95a | 2015-03-20 17:26:30 -0700 | [diff] [blame] | 48 | import com.android.launcher3.util.WallpaperUtils; |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 49 | import com.android.photos.BitmapRegionTileSource; |
Michael Jurka | 619a180 | 2013-10-29 15:50:06 +0100 | [diff] [blame] | 50 | import com.android.photos.BitmapRegionTileSource.BitmapSource; |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 51 | import com.android.photos.BitmapRegionTileSource.BitmapSource.InBitmapProvider; |
| 52 | import com.android.photos.views.TiledImageRenderer.TileSource; |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 53 | |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 54 | import java.util.Collections; |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 55 | import java.util.Set; |
| 56 | import java.util.WeakHashMap; |
| 57 | |
Sunny Goyal | 1d0b093 | 2015-03-20 18:51:38 -0700 | [diff] [blame^] | 58 | public class WallpaperCropActivity extends BaseActivity implements Handler.Callback { |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 59 | private static final String LOGTAG = "Launcher3.CropActivity"; |
| 60 | |
Sunny Goyal | 6a1e95a | 2015-03-20 17:26:30 -0700 | [diff] [blame] | 61 | protected static final String WALLPAPER_WIDTH_KEY = WallpaperUtils.WALLPAPER_WIDTH_KEY; |
| 62 | protected static final String WALLPAPER_HEIGHT_KEY = WallpaperUtils.WALLPAPER_HEIGHT_KEY; |
Sunny Goyal | 06a4d3f | 2015-02-12 11:19:39 -0800 | [diff] [blame] | 63 | |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 64 | /** |
| 65 | * The maximum bitmap size we allow to be returned through the intent. |
| 66 | * Intents have a maximum of 1MB in total size. However, the Bitmap seems to |
| 67 | * have some overhead to hit so that we go way below the limit here to make |
| 68 | * sure the intent stays below 1MB.We should consider just returning a byte |
| 69 | * array instead of a Bitmap instance to avoid overhead. |
| 70 | */ |
| 71 | public static final int MAX_BMAP_IN_INTENT = 750000; |
Sunny Goyal | 6a1e95a | 2015-03-20 17:26:30 -0700 | [diff] [blame] | 72 | public static final float WALLPAPER_SCREENS_SPAN = WallpaperUtils.WALLPAPER_SCREENS_SPAN; |
Michael Jurka | a6a0547 | 2013-11-13 17:59:46 +0100 | [diff] [blame] | 73 | |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 74 | private static final int MSG_LOAD_IMAGE = 1; |
| 75 | |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 76 | protected CropView mCropView; |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 77 | protected View mProgressView; |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 78 | protected Uri mUri; |
Selim Cinek | ffed658 | 2014-02-28 17:18:59 +0100 | [diff] [blame] | 79 | protected View mSetWallpaperButton; |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 80 | |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 81 | private HandlerThread mLoaderThread; |
| 82 | private Handler mLoaderHandler; |
Adam Cohen | 091440a | 2015-03-18 14:16:05 -0700 | [diff] [blame] | 83 | @Thunk LoadRequest mCurrentLoadRequest; |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 84 | private byte[] mTempStorageForDecoding = new byte[16 * 1024]; |
| 85 | // A weak-set of reusable bitmaps |
Adam Cohen | 091440a | 2015-03-18 14:16:05 -0700 | [diff] [blame] | 86 | @Thunk Set<Bitmap> mReusableBitmaps = |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 87 | Collections.newSetFromMap(new WeakHashMap<Bitmap, Boolean>()); |
| 88 | |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 89 | @Override |
Sunny Goyal | 1d0b093 | 2015-03-20 18:51:38 -0700 | [diff] [blame^] | 90 | public void onCreate(Bundle savedInstanceState) { |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 91 | super.onCreate(savedInstanceState); |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 92 | |
| 93 | mLoaderThread = new HandlerThread("wallpaper_loader"); |
| 94 | mLoaderThread.start(); |
| 95 | mLoaderHandler = new Handler(mLoaderThread.getLooper(), this); |
| 96 | |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 97 | init(); |
Michael Jurka | 7267fa5 | 2013-09-26 15:29:57 -0700 | [diff] [blame] | 98 | if (!enableRotation()) { |
| 99 | setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT); |
| 100 | } |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 101 | } |
| 102 | |
| 103 | protected void init() { |
| 104 | setContentView(R.layout.wallpaper_cropper); |
| 105 | |
| 106 | mCropView = (CropView) findViewById(R.id.cropView); |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 107 | mProgressView = findViewById(R.id.loading); |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 108 | |
Michael Jurka | f6502c3 | 2013-10-14 14:09:21 -0700 | [diff] [blame] | 109 | Intent cropIntent = getIntent(); |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 110 | final Uri imageUri = cropIntent.getData(); |
| 111 | |
Michael Jurka | f6502c3 | 2013-10-14 14:09:21 -0700 | [diff] [blame] | 112 | if (imageUri == null) { |
| 113 | Log.e(LOGTAG, "No URI passed in intent, exiting WallpaperCropActivity"); |
| 114 | finish(); |
| 115 | return; |
| 116 | } |
| 117 | |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 118 | // Action bar |
| 119 | // Show the custom action bar view |
| 120 | final ActionBar actionBar = getActionBar(); |
| 121 | actionBar.setCustomView(R.layout.actionbar_set_wallpaper); |
| 122 | actionBar.getCustomView().setOnClickListener( |
| 123 | new View.OnClickListener() { |
| 124 | @Override |
| 125 | public void onClick(View v) { |
| 126 | boolean finishActivityWhenDone = true; |
Michael Jurka | be2f8dd | 2013-09-10 13:39:59 +0200 | [diff] [blame] | 127 | cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone); |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 128 | } |
| 129 | }); |
Michael Jurka | 849aff7 | 2014-01-16 14:58:00 +0100 | [diff] [blame] | 130 | mSetWallpaperButton = findViewById(R.id.set_wallpaper_button); |
Michael Jurka | 862f7e3 | 2013-10-16 18:23:56 -0700 | [diff] [blame] | 131 | |
| 132 | // Load image in background |
Michael Jurka | 619a180 | 2013-10-29 15:50:06 +0100 | [diff] [blame] | 133 | final BitmapRegionTileSource.UriBitmapSource bitmapSource = |
Sunny Goyal | 1d0b093 | 2015-03-20 18:51:38 -0700 | [diff] [blame^] | 134 | new BitmapRegionTileSource.UriBitmapSource(getContext(), imageUri, 1024); |
Selim Cinek | ffed658 | 2014-02-28 17:18:59 +0100 | [diff] [blame] | 135 | mSetWallpaperButton.setEnabled(false); |
Michael Jurka | 619a180 | 2013-10-29 15:50:06 +0100 | [diff] [blame] | 136 | Runnable onLoad = new Runnable() { |
| 137 | public void run() { |
| 138 | if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) { |
Sunny Goyal | 1d0b093 | 2015-03-20 18:51:38 -0700 | [diff] [blame^] | 139 | Toast.makeText(getContext(), R.string.wallpaper_load_fail, |
Michael Jurka | 619a180 | 2013-10-29 15:50:06 +0100 | [diff] [blame] | 140 | Toast.LENGTH_LONG).show(); |
| 141 | finish(); |
Michael Jurka | 849aff7 | 2014-01-16 14:58:00 +0100 | [diff] [blame] | 142 | } else { |
Selim Cinek | ffed658 | 2014-02-28 17:18:59 +0100 | [diff] [blame] | 143 | mSetWallpaperButton.setEnabled(true); |
Michael Jurka | 619a180 | 2013-10-29 15:50:06 +0100 | [diff] [blame] | 144 | } |
| 145 | } |
| 146 | }; |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 147 | setCropViewTileSource(bitmapSource, true, false, null, onLoad); |
Michael Jurka | 862f7e3 | 2013-10-16 18:23:56 -0700 | [diff] [blame] | 148 | } |
| 149 | |
Sunny Goyal | e9bacd9 | 2014-07-22 09:39:01 -0700 | [diff] [blame] | 150 | @Override |
Sunny Goyal | 1d0b093 | 2015-03-20 18:51:38 -0700 | [diff] [blame^] | 151 | public void onDestroy() { |
Sunny Goyal | e9bacd9 | 2014-07-22 09:39:01 -0700 | [diff] [blame] | 152 | if (mCropView != null) { |
| 153 | mCropView.destroy(); |
| 154 | } |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 155 | if (mLoaderThread != null) { |
| 156 | mLoaderThread.quit(); |
| 157 | } |
Sunny Goyal | e9bacd9 | 2014-07-22 09:39:01 -0700 | [diff] [blame] | 158 | super.onDestroy(); |
| 159 | } |
| 160 | |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 161 | /** |
| 162 | * This is called on {@link #mLoaderThread} |
| 163 | */ |
| 164 | @Override |
| 165 | public boolean handleMessage(Message msg) { |
| 166 | if (msg.what == MSG_LOAD_IMAGE) { |
| 167 | final LoadRequest req = (LoadRequest) msg.obj; |
| 168 | try { |
| 169 | req.src.loadInBackground(new InBitmapProvider() { |
| 170 | |
| 171 | @Override |
| 172 | public Bitmap forPixelCount(int count) { |
Sunny Goyal | 9f9d0a5 | 2015-02-25 11:34:17 -0800 | [diff] [blame] | 173 | Bitmap bitmapToReuse = null; |
| 174 | // Find the smallest bitmap that satisfies the pixel count limit |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 175 | synchronized (mReusableBitmaps) { |
Sunny Goyal | 9f9d0a5 | 2015-02-25 11:34:17 -0800 | [diff] [blame] | 176 | int currentBitmapSize = Integer.MAX_VALUE; |
| 177 | for (Bitmap b : mReusableBitmaps) { |
| 178 | int bitmapSize = b.getWidth() * b.getHeight(); |
| 179 | if ((bitmapSize >= count) && (bitmapSize < currentBitmapSize)) { |
| 180 | bitmapToReuse = b; |
| 181 | currentBitmapSize = bitmapSize; |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 182 | } |
| 183 | } |
Sunny Goyal | 9f9d0a5 | 2015-02-25 11:34:17 -0800 | [diff] [blame] | 184 | |
| 185 | if (bitmapToReuse != null) { |
| 186 | mReusableBitmaps.remove(bitmapToReuse); |
| 187 | } |
Selim Cinek | 459875d | 2014-03-04 21:29:40 +0100 | [diff] [blame] | 188 | } |
Sunny Goyal | 9f9d0a5 | 2015-02-25 11:34:17 -0800 | [diff] [blame] | 189 | return bitmapToReuse; |
Selim Cinek | 459875d | 2014-03-04 21:29:40 +0100 | [diff] [blame] | 190 | } |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 191 | }); |
| 192 | } catch (SecurityException securityException) { |
| 193 | if (isDestroyed()) { |
| 194 | // Temporarily granted permissions are revoked when the activity |
| 195 | // finishes, potentially resulting in a SecurityException here. |
| 196 | // Even though {@link #isDestroyed} might also return true in different |
| 197 | // situations where the configuration changes, we are fine with |
| 198 | // catching these cases here as well. |
| 199 | return true; |
| 200 | } else { |
| 201 | // otherwise it had a different cause and we throw it further |
| 202 | throw securityException; |
Michael Jurka | 619a180 | 2013-10-29 15:50:06 +0100 | [diff] [blame] | 203 | } |
Michael Jurka | 862f7e3 | 2013-10-16 18:23:56 -0700 | [diff] [blame] | 204 | } |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 205 | |
Sunny Goyal | 1d0b093 | 2015-03-20 18:51:38 -0700 | [diff] [blame^] | 206 | req.result = new BitmapRegionTileSource(getContext(), req.src, mTempStorageForDecoding); |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 207 | runOnUiThread(new Runnable() { |
| 208 | |
| 209 | @Override |
| 210 | public void run() { |
| 211 | if (req == mCurrentLoadRequest) { |
| 212 | onLoadRequestComplete(req, |
| 213 | req.src.getLoadingState() == BitmapSource.State.LOADED); |
| 214 | } else { |
| 215 | addReusableBitmap(req.result); |
| 216 | } |
| 217 | } |
| 218 | }); |
| 219 | return true; |
| 220 | } |
| 221 | return false; |
| 222 | } |
| 223 | |
Adam Cohen | 091440a | 2015-03-18 14:16:05 -0700 | [diff] [blame] | 224 | @Thunk void addReusableBitmap(TileSource src) { |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 225 | synchronized (mReusableBitmaps) { |
| 226 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT |
| 227 | && src instanceof BitmapRegionTileSource) { |
| 228 | Bitmap preview = ((BitmapRegionTileSource) src).getBitmap(); |
| 229 | if (preview != null && preview.isMutable()) { |
| 230 | mReusableBitmaps.add(preview); |
| 231 | } |
| 232 | } |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | protected void onLoadRequestComplete(LoadRequest req, boolean success) { |
| 237 | mCurrentLoadRequest = null; |
| 238 | if (success) { |
| 239 | TileSource oldSrc = mCropView.getTileSource(); |
| 240 | mCropView.setTileSource(req.result, null); |
| 241 | mCropView.setTouchEnabled(req.touchEnabled); |
| 242 | if (req.moveToLeft) { |
| 243 | mCropView.moveToLeft(); |
| 244 | } |
| 245 | if (req.scaleProvider != null) { |
| 246 | mCropView.setScale(req.scaleProvider.getScale(req.result)); |
| 247 | } |
| 248 | |
| 249 | // Free last image |
| 250 | if (oldSrc != null) { |
| 251 | // Call yield instead of recycle, as we only want to free GL resource. |
| 252 | // We can still reuse the bitmap for decoding any other image. |
| 253 | oldSrc.getPreview().yield(); |
| 254 | } |
| 255 | addReusableBitmap(oldSrc); |
| 256 | } |
| 257 | if (req.postExecute != null) { |
| 258 | req.postExecute.run(); |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | public final void setCropViewTileSource(BitmapSource bitmapSource, boolean touchEnabled, |
| 263 | boolean moveToLeft, CropViewScaleProvider scaleProvider, Runnable postExecute) { |
| 264 | final LoadRequest req = new LoadRequest(); |
| 265 | req.moveToLeft = moveToLeft; |
| 266 | req.src = bitmapSource; |
| 267 | req.touchEnabled = touchEnabled; |
| 268 | req.postExecute = postExecute; |
| 269 | req.scaleProvider = scaleProvider; |
| 270 | mCurrentLoadRequest = req; |
| 271 | |
| 272 | // Remove any pending requests |
| 273 | mLoaderHandler.removeMessages(MSG_LOAD_IMAGE); |
| 274 | Message.obtain(mLoaderHandler, MSG_LOAD_IMAGE, req).sendToTarget(); |
| 275 | |
Michael Jurka | 862f7e3 | 2013-10-16 18:23:56 -0700 | [diff] [blame] | 276 | // We don't want to show the spinner every time we load an image, because that would be |
| 277 | // annoying; instead, only start showing the spinner if loading the image has taken |
| 278 | // longer than 1 sec (ie 1000 ms) |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 279 | mProgressView.postDelayed(new Runnable() { |
Michael Jurka | 862f7e3 | 2013-10-16 18:23:56 -0700 | [diff] [blame] | 280 | public void run() { |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 281 | if (mCurrentLoadRequest == req) { |
| 282 | mProgressView.setVisibility(View.VISIBLE); |
Michael Jurka | 862f7e3 | 2013-10-16 18:23:56 -0700 | [diff] [blame] | 283 | } |
| 284 | } |
| 285 | }, 1000); |
Michael Jurka | 7267fa5 | 2013-09-26 15:29:57 -0700 | [diff] [blame] | 286 | } |
| 287 | |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 288 | |
Michael Jurka | 7267fa5 | 2013-09-26 15:29:57 -0700 | [diff] [blame] | 289 | public boolean enableRotation() { |
| 290 | return getResources().getBoolean(R.bool.allow_rotation); |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 291 | } |
| 292 | |
Sunny Goyal | cdc4dbd | 2014-07-16 16:04:08 -0700 | [diff] [blame] | 293 | protected void setWallpaper(Uri uri, final boolean finishActivityWhenDone) { |
Sunny Goyal | 1d0b093 | 2015-03-20 18:51:38 -0700 | [diff] [blame^] | 294 | int rotation = BitmapUtils.getRotationFromExif(getContext(), uri); |
Michael Jurka | 8713a94 | 2013-10-10 21:28:40 -0700 | [diff] [blame] | 295 | BitmapCropTask cropTask = new BitmapCropTask( |
Sunny Goyal | 1d0b093 | 2015-03-20 18:51:38 -0700 | [diff] [blame^] | 296 | getContext(), uri, null, rotation, 0, 0, true, false, null); |
Michael Jurka | be2f8dd | 2013-09-10 13:39:59 +0200 | [diff] [blame] | 297 | final Point bounds = cropTask.getImageBounds(); |
| 298 | Runnable onEndCrop = new Runnable() { |
| 299 | public void run() { |
| 300 | updateWallpaperDimensions(bounds.x, bounds.y); |
| 301 | if (finishActivityWhenDone) { |
| 302 | setResult(Activity.RESULT_OK); |
| 303 | finish(); |
| 304 | } |
| 305 | } |
| 306 | }; |
| 307 | cropTask.setOnEndRunnable(onEndCrop); |
| 308 | cropTask.setNoCrop(true); |
| 309 | cropTask.execute(); |
| 310 | } |
| 311 | |
| 312 | protected void cropImageAndSetWallpaper( |
| 313 | Resources res, int resId, final boolean finishActivityWhenDone) { |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 314 | // crop this image and scale it down to the default wallpaper size for |
| 315 | // this device |
Sunny Goyal | 06a4d3f | 2015-02-12 11:19:39 -0800 | [diff] [blame] | 316 | int rotation = BitmapUtils.getRotationFromExif(res, resId); |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 317 | Point inSize = mCropView.getSourceDimensions(); |
Sunny Goyal | 6a1e95a | 2015-03-20 17:26:30 -0700 | [diff] [blame] | 318 | Point outSize = WallpaperUtils.getDefaultWallpaperSize(getResources(), |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 319 | getWindowManager()); |
Sunny Goyal | 06a4d3f | 2015-02-12 11:19:39 -0800 | [diff] [blame] | 320 | RectF crop = Utils.getMaxCropRect( |
Michael Jurka | be2f8dd | 2013-09-10 13:39:59 +0200 | [diff] [blame] | 321 | inSize.x, inSize.y, outSize.x, outSize.y, false); |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 322 | Runnable onEndCrop = new Runnable() { |
| 323 | public void run() { |
| 324 | // Passing 0, 0 will cause launcher to revert to using the |
| 325 | // default wallpaper size |
| 326 | updateWallpaperDimensions(0, 0); |
| 327 | if (finishActivityWhenDone) { |
| 328 | setResult(Activity.RESULT_OK); |
| 329 | finish(); |
| 330 | } |
| 331 | } |
| 332 | }; |
Sunny Goyal | 1d0b093 | 2015-03-20 18:51:38 -0700 | [diff] [blame^] | 333 | BitmapCropTask cropTask = new BitmapCropTask(getContext(), res, resId, |
Michael Jurka | 8713a94 | 2013-10-10 21:28:40 -0700 | [diff] [blame] | 334 | crop, rotation, outSize.x, outSize.y, true, false, onEndCrop); |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 335 | cropTask.execute(); |
| 336 | } |
| 337 | |
Sunny Goyal | 06a4d3f | 2015-02-12 11:19:39 -0800 | [diff] [blame] | 338 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) |
Michael Jurka | be2f8dd | 2013-09-10 13:39:59 +0200 | [diff] [blame] | 339 | protected void cropImageAndSetWallpaper(Uri uri, |
Sunny Goyal | 06a4d3f | 2015-02-12 11:19:39 -0800 | [diff] [blame] | 340 | BitmapCropTask.OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) { |
Michael Jurka | 4808aa6 | 2014-01-14 13:50:53 +0100 | [diff] [blame] | 341 | boolean centerCrop = getResources().getBoolean(R.bool.center_crop); |
Michael Jurka | fe0ace3 | 2013-10-03 01:05:14 -0700 | [diff] [blame] | 342 | // Get the crop |
Michael Jurka | fe0ace3 | 2013-10-03 01:05:14 -0700 | [diff] [blame] | 343 | boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR; |
| 344 | |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 345 | Display d = getWindowManager().getDefaultDisplay(); |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 346 | |
| 347 | Point displaySize = new Point(); |
| 348 | d.getSize(displaySize); |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 349 | boolean isPortrait = displaySize.x < displaySize.y; |
Michael Jurka | 454db97 | 2013-10-31 16:32:43 +0100 | [diff] [blame] | 350 | |
Sunny Goyal | 6a1e95a | 2015-03-20 17:26:30 -0700 | [diff] [blame] | 351 | Point defaultWallpaperSize = WallpaperUtils.getDefaultWallpaperSize(getResources(), |
Michael Jurka | 454db97 | 2013-10-31 16:32:43 +0100 | [diff] [blame] | 352 | getWindowManager()); |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 353 | // Get the crop |
| 354 | RectF cropRect = mCropView.getCrop(); |
Selim Cinek | e04e8b2 | 2014-03-14 18:19:35 +0100 | [diff] [blame] | 355 | |
Selim Cinek | 7c989e9 | 2014-03-24 14:13:20 +0100 | [diff] [blame] | 356 | Point inSize = mCropView.getSourceDimensions(); |
| 357 | |
Michael Jurka | 8713a94 | 2013-10-10 21:28:40 -0700 | [diff] [blame] | 358 | int cropRotation = mCropView.getImageRotation(); |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 359 | float cropScale = mCropView.getWidth() / (float) cropRect.width(); |
| 360 | |
Selim Cinek | 7c989e9 | 2014-03-24 14:13:20 +0100 | [diff] [blame] | 361 | |
Michael Jurka | 8713a94 | 2013-10-10 21:28:40 -0700 | [diff] [blame] | 362 | Matrix rotateMatrix = new Matrix(); |
| 363 | rotateMatrix.setRotate(cropRotation); |
| 364 | float[] rotatedInSize = new float[] { inSize.x, inSize.y }; |
| 365 | rotateMatrix.mapPoints(rotatedInSize); |
| 366 | rotatedInSize[0] = Math.abs(rotatedInSize[0]); |
| 367 | rotatedInSize[1] = Math.abs(rotatedInSize[1]); |
| 368 | |
Selim Cinek | 3144256 | 2014-03-27 20:35:19 +0100 | [diff] [blame] | 369 | |
| 370 | // due to rounding errors in the cropview renderer the edges can be slightly offset |
| 371 | // therefore we ensure that the boundaries are sanely defined |
| 372 | cropRect.left = Math.max(0, cropRect.left); |
| 373 | cropRect.right = Math.min(rotatedInSize[0], cropRect.right); |
| 374 | cropRect.top = Math.max(0, cropRect.top); |
| 375 | cropRect.bottom = Math.min(rotatedInSize[1], cropRect.bottom); |
| 376 | |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 377 | // ADJUST CROP WIDTH |
| 378 | // Extend the crop all the way to the right, for parallax |
Michael Jurka | fe0ace3 | 2013-10-03 01:05:14 -0700 | [diff] [blame] | 379 | // (or all the way to the left, in RTL) |
Michael Jurka | 4808aa6 | 2014-01-14 13:50:53 +0100 | [diff] [blame] | 380 | float extraSpace; |
| 381 | if (centerCrop) { |
| 382 | extraSpace = 2f * Math.min(rotatedInSize[0] - cropRect.right, cropRect.left); |
| 383 | } else { |
| 384 | extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left; |
| 385 | } |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 386 | // Cap the amount of extra width |
Michael Jurka | 454db97 | 2013-10-31 16:32:43 +0100 | [diff] [blame] | 387 | float maxExtraSpace = defaultWallpaperSize.x / cropScale - cropRect.width(); |
Michael Jurka | fe0ace3 | 2013-10-03 01:05:14 -0700 | [diff] [blame] | 388 | extraSpace = Math.min(extraSpace, maxExtraSpace); |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 389 | |
Michael Jurka | 4808aa6 | 2014-01-14 13:50:53 +0100 | [diff] [blame] | 390 | if (centerCrop) { |
| 391 | cropRect.left -= extraSpace / 2f; |
| 392 | cropRect.right += extraSpace / 2f; |
Michael Jurka | fe0ace3 | 2013-10-03 01:05:14 -0700 | [diff] [blame] | 393 | } else { |
Michael Jurka | 4808aa6 | 2014-01-14 13:50:53 +0100 | [diff] [blame] | 394 | if (ltr) { |
| 395 | cropRect.right += extraSpace; |
| 396 | } else { |
| 397 | cropRect.left -= extraSpace; |
| 398 | } |
Michael Jurka | fe0ace3 | 2013-10-03 01:05:14 -0700 | [diff] [blame] | 399 | } |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 400 | |
| 401 | // ADJUST CROP HEIGHT |
| 402 | if (isPortrait) { |
Michael Jurka | 454db97 | 2013-10-31 16:32:43 +0100 | [diff] [blame] | 403 | cropRect.bottom = cropRect.top + defaultWallpaperSize.y / cropScale; |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 404 | } else { // LANDSCAPE |
| 405 | float extraPortraitHeight = |
Michael Jurka | 454db97 | 2013-10-31 16:32:43 +0100 | [diff] [blame] | 406 | defaultWallpaperSize.y / cropScale - cropRect.height(); |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 407 | float expandHeight = |
Michael Jurka | 8713a94 | 2013-10-10 21:28:40 -0700 | [diff] [blame] | 408 | Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top), |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 409 | extraPortraitHeight / 2); |
| 410 | cropRect.top -= expandHeight; |
| 411 | cropRect.bottom += expandHeight; |
| 412 | } |
| 413 | final int outWidth = (int) Math.round(cropRect.width() * cropScale); |
| 414 | final int outHeight = (int) Math.round(cropRect.height() * cropScale); |
| 415 | |
| 416 | Runnable onEndCrop = new Runnable() { |
| 417 | public void run() { |
| 418 | updateWallpaperDimensions(outWidth, outHeight); |
| 419 | if (finishActivityWhenDone) { |
| 420 | setResult(Activity.RESULT_OK); |
| 421 | finish(); |
| 422 | } |
| 423 | } |
| 424 | }; |
Sunny Goyal | 1d0b093 | 2015-03-20 18:51:38 -0700 | [diff] [blame^] | 425 | BitmapCropTask cropTask = new BitmapCropTask(getContext(), uri, |
Michael Jurka | 8713a94 | 2013-10-10 21:28:40 -0700 | [diff] [blame] | 426 | cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop); |
Michael Jurka | be2f8dd | 2013-09-10 13:39:59 +0200 | [diff] [blame] | 427 | if (onBitmapCroppedHandler != null) { |
| 428 | cropTask.setOnBitmapCropped(onBitmapCroppedHandler); |
| 429 | } |
Michael Jurka | 2d8de58 | 2013-09-04 14:42:52 +0200 | [diff] [blame] | 430 | cropTask.execute(); |
| 431 | } |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 432 | |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 433 | protected void updateWallpaperDimensions(int width, int height) { |
Sunny Goyal | 6a1e95a | 2015-03-20 17:26:30 -0700 | [diff] [blame] | 434 | String spKey = LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY; |
Sunny Goyal | 1d0b093 | 2015-03-20 18:51:38 -0700 | [diff] [blame^] | 435 | SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS); |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 436 | SharedPreferences.Editor editor = sp.edit(); |
| 437 | if (width != 0 && height != 0) { |
| 438 | editor.putInt(WALLPAPER_WIDTH_KEY, width); |
| 439 | editor.putInt(WALLPAPER_HEIGHT_KEY, height); |
| 440 | } else { |
| 441 | editor.remove(WALLPAPER_WIDTH_KEY); |
| 442 | editor.remove(WALLPAPER_HEIGHT_KEY); |
| 443 | } |
| 444 | editor.commit(); |
Sunny Goyal | 6a1e95a | 2015-03-20 17:26:30 -0700 | [diff] [blame] | 445 | WallpaperUtils.suggestWallpaperDimension(getResources(), |
Sunny Goyal | 1d0b093 | 2015-03-20 18:51:38 -0700 | [diff] [blame^] | 446 | sp, getWindowManager(), WallpaperManager.getInstance(getContext()), true); |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 447 | } |
| 448 | |
Sunny Goyal | 65b929d | 2015-02-12 15:34:41 -0800 | [diff] [blame] | 449 | static class LoadRequest { |
| 450 | BitmapSource src; |
| 451 | boolean touchEnabled; |
| 452 | boolean moveToLeft; |
| 453 | Runnable postExecute; |
| 454 | CropViewScaleProvider scaleProvider; |
| 455 | |
| 456 | TileSource result; |
| 457 | } |
| 458 | |
| 459 | interface CropViewScaleProvider { |
| 460 | float getScale(TileSource src); |
| 461 | } |
Michael Jurka | 104c456 | 2013-07-08 18:03:46 -0700 | [diff] [blame] | 462 | } |