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 19 import android.animation.LayoutTransition; 20 import android.annotation.TargetApi; 21 import android.app.ActionBar; 22 import android.app.Activity; 23 import android.app.WallpaperInfo; 24 import android.app.WallpaperManager; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.ApplicationInfo; 28 import android.content.pm.PackageManager; 29 import android.content.res.Resources; 30 import android.database.Cursor; 31 import android.database.DataSetObserver; 32 import android.graphics.Bitmap; 33 import android.graphics.BitmapFactory; 34 import android.graphics.Canvas; 35 import android.graphics.Matrix; 36 import android.graphics.Point; 37 import android.graphics.PorterDuff; 38 import android.graphics.Rect; 39 import android.graphics.RectF; 40 import android.graphics.drawable.BitmapDrawable; 41 import android.graphics.drawable.Drawable; 42 import android.graphics.drawable.LevelListDrawable; 43 import android.net.Uri; 44 import android.os.AsyncTask; 45 import android.os.Build; 46 import android.os.Bundle; 47 import android.provider.MediaStore; 48 import android.util.Log; 49 import android.util.Pair; 50 import android.view.ActionMode; 51 import android.view.LayoutInflater; 52 import android.view.Menu; 53 import android.view.MenuInflater; 54 import android.view.MenuItem; 55 import android.view.View; 56 import android.view.View.OnClickListener; 57 import android.view.View.OnLayoutChangeListener; 58 import android.view.ViewGroup; 59 import android.view.ViewPropertyAnimator; 60 import android.view.ViewTreeObserver; 61 import android.view.ViewTreeObserver.OnGlobalLayoutListener; 62 import android.view.WindowManager; 63 import android.view.animation.AccelerateInterpolator; 64 import android.view.animation.DecelerateInterpolator; 65 import android.widget.ArrayAdapter; 66 import android.widget.BaseAdapter; 67 import android.widget.FrameLayout; 68 import android.widget.HorizontalScrollView; 69 import android.widget.ImageView; 70 import android.widget.LinearLayout; 71 import android.widget.Toast; 72 73 import com.android.photos.BitmapRegionTileSource; 74 import com.android.photos.BitmapRegionTileSource.BitmapSource; 75 76 import java.io.File; 77 import java.io.FileOutputStream; 78 import java.io.IOException; 79 import java.util.ArrayList; 80 81 public class WallpaperPickerActivity extends WallpaperCropActivity { 82 static final String TAG = "Launcher.WallpaperPickerActivity"; 83 84 public static final int IMAGE_PICK = 5; 85 public static final int PICK_WALLPAPER_THIRD_PARTY_ACTIVITY = 6; 86 public static final int PICK_LIVE_WALLPAPER = 7; 87 private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES"; 88 private static final String SELECTED_INDEX = "SELECTED_INDEX"; 89 private static final String OLD_DEFAULT_WALLPAPER_THUMBNAIL_FILENAME = "default_thumb.jpg"; 90 private static final String DEFAULT_WALLPAPER_THUMBNAIL_FILENAME = "default_thumb2.jpg"; 91 private static final int FLAG_POST_DELAY_MILLIS = 200; 92 93 private View mSelectedTile; 94 private boolean mIgnoreNextTap; 95 private OnClickListener mThumbnailOnClickListener; 96 97 private LinearLayout mWallpapersView; 98 private View mWallpaperStrip; 99 100 private ActionMode.Callback mActionModeCallback; 101 private ActionMode mActionMode; 102 103 private View.OnLongClickListener mLongClickListener; 104 105 ArrayList<Uri> mTempWallpaperTiles = new ArrayList<Uri>(); 106 private SavedWallpaperImages mSavedImages; 107 private WallpaperInfo mLiveWallpaperInfoOnPickerLaunch; 108 private int mSelectedIndex = -1; 109 private WallpaperInfo mLastClickedLiveWallpaperInfo; 110 111 public static abstract class WallpaperTileInfo { 112 protected View mView; 113 public Drawable mThumb; 114 setView(View v)115 public void setView(View v) { 116 mView = v; 117 } onClick(WallpaperPickerActivity a)118 public void onClick(WallpaperPickerActivity a) {} onSave(WallpaperPickerActivity a)119 public void onSave(WallpaperPickerActivity a) {} onDelete(WallpaperPickerActivity a)120 public void onDelete(WallpaperPickerActivity a) {} isSelectable()121 public boolean isSelectable() { return false; } isNamelessWallpaper()122 public boolean isNamelessWallpaper() { return false; } onIndexUpdated(CharSequence label)123 public void onIndexUpdated(CharSequence label) { 124 if (isNamelessWallpaper()) { 125 mView.setContentDescription(label); 126 } 127 } 128 } 129 130 public static class PickImageInfo extends WallpaperTileInfo { 131 @Override onClick(WallpaperPickerActivity a)132 public void onClick(WallpaperPickerActivity a) { 133 Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 134 intent.setType("image/*"); 135 a.startActivityForResultSafely(intent, IMAGE_PICK); 136 } 137 } 138 139 public static class UriWallpaperInfo extends WallpaperTileInfo { 140 private Uri mUri; 141 private boolean mFirstClick = true; 142 private BitmapRegionTileSource.UriBitmapSource mBitmapSource; UriWallpaperInfo(Uri uri)143 public UriWallpaperInfo(Uri uri) { 144 mUri = uri; 145 } 146 @Override onClick(final WallpaperPickerActivity a)147 public void onClick(final WallpaperPickerActivity a) { 148 final Runnable onLoad; 149 if (!mFirstClick) { 150 onLoad = null; 151 } else { 152 mFirstClick = false; 153 a.mSetWallpaperButton.setEnabled(false); 154 onLoad = new Runnable() { 155 public void run() { 156 if (mBitmapSource != null && 157 mBitmapSource.getLoadingState() == BitmapSource.State.LOADED) { 158 a.selectTile(mView); 159 a.mSetWallpaperButton.setEnabled(true); 160 } else { 161 ViewGroup parent = (ViewGroup) mView.getParent(); 162 if (parent != null) { 163 parent.removeView(mView); 164 Toast.makeText(a, 165 a.getString(R.string.image_load_fail), 166 Toast.LENGTH_SHORT).show(); 167 } 168 } 169 } 170 }; 171 } 172 mBitmapSource = new BitmapRegionTileSource.UriBitmapSource( 173 a, mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE); 174 a.setCropViewTileSource(mBitmapSource, true, false, onLoad); 175 } 176 @Override onSave(final WallpaperPickerActivity a)177 public void onSave(final WallpaperPickerActivity a) { 178 boolean finishActivityWhenDone = true; 179 OnBitmapCroppedHandler h = new OnBitmapCroppedHandler() { 180 public void onBitmapCropped(byte[] imageBytes) { 181 Point thumbSize = getDefaultThumbnailSize(a.getResources()); 182 // rotation is set to 0 since imageBytes has already been correctly rotated 183 Bitmap thumb = createThumbnail( 184 thumbSize, null, null, imageBytes, null, 0, 0, true); 185 a.getSavedImages().writeImage(thumb, imageBytes); 186 } 187 }; 188 a.cropImageAndSetWallpaper(mUri, h, finishActivityWhenDone); 189 } 190 @Override isSelectable()191 public boolean isSelectable() { 192 return true; 193 } 194 @Override isNamelessWallpaper()195 public boolean isNamelessWallpaper() { 196 return true; 197 } 198 } 199 200 public static class FileWallpaperInfo extends WallpaperTileInfo { 201 private File mFile; 202 FileWallpaperInfo(File target, Drawable thumb)203 public FileWallpaperInfo(File target, Drawable thumb) { 204 mFile = target; 205 mThumb = thumb; 206 } 207 @Override onClick(WallpaperPickerActivity a)208 public void onClick(WallpaperPickerActivity a) { 209 BitmapRegionTileSource.UriBitmapSource bitmapSource = 210 new BitmapRegionTileSource.UriBitmapSource(a, Uri.fromFile(mFile), 1024); 211 a.setCropViewTileSource(bitmapSource, false, true, null); 212 } 213 @Override onSave(WallpaperPickerActivity a)214 public void onSave(WallpaperPickerActivity a) { 215 a.setWallpaper(Uri.fromFile(mFile), true); 216 } 217 @Override isSelectable()218 public boolean isSelectable() { 219 return true; 220 } 221 @Override isNamelessWallpaper()222 public boolean isNamelessWallpaper() { 223 return true; 224 } 225 } 226 227 public static class ResourceWallpaperInfo extends WallpaperTileInfo { 228 private Resources mResources; 229 private int mResId; 230 ResourceWallpaperInfo(Resources res, int resId, Drawable thumb)231 public ResourceWallpaperInfo(Resources res, int resId, Drawable thumb) { 232 mResources = res; 233 mResId = resId; 234 mThumb = thumb; 235 } 236 @Override onClick(WallpaperPickerActivity a)237 public void onClick(WallpaperPickerActivity a) { 238 BitmapRegionTileSource.ResourceBitmapSource bitmapSource = 239 new BitmapRegionTileSource.ResourceBitmapSource( 240 mResources, mResId, BitmapRegionTileSource.MAX_PREVIEW_SIZE); 241 bitmapSource.loadInBackground(); 242 BitmapRegionTileSource source = new BitmapRegionTileSource(a, bitmapSource); 243 CropView v = a.getCropView(); 244 v.setTileSource(source, null); 245 Point wallpaperSize = WallpaperCropActivity.getDefaultWallpaperSize( 246 a.getResources(), a.getWindowManager()); 247 RectF crop = WallpaperCropActivity.getMaxCropRect( 248 source.getImageWidth(), source.getImageHeight(), 249 wallpaperSize.x, wallpaperSize.y, false); 250 v.setScale(wallpaperSize.x / crop.width()); 251 v.setTouchEnabled(false); 252 a.setSystemWallpaperVisiblity(false); 253 } 254 @Override onSave(WallpaperPickerActivity a)255 public void onSave(WallpaperPickerActivity a) { 256 boolean finishActivityWhenDone = true; 257 a.cropImageAndSetWallpaper(mResources, mResId, finishActivityWhenDone); 258 } 259 @Override isSelectable()260 public boolean isSelectable() { 261 return true; 262 } 263 @Override isNamelessWallpaper()264 public boolean isNamelessWallpaper() { 265 return true; 266 } 267 } 268 269 @TargetApi(Build.VERSION_CODES.KITKAT) 270 public static class DefaultWallpaperInfo extends WallpaperTileInfo { DefaultWallpaperInfo(Drawable thumb)271 public DefaultWallpaperInfo(Drawable thumb) { 272 mThumb = thumb; 273 } 274 @Override onClick(WallpaperPickerActivity a)275 public void onClick(WallpaperPickerActivity a) { 276 CropView c = a.getCropView(); 277 278 Drawable defaultWallpaper = WallpaperManager.getInstance(a).getBuiltInDrawable( 279 c.getWidth(), c.getHeight(), false, 0.5f, 0.5f); 280 281 if (defaultWallpaper == null) { 282 Log.w(TAG, "Null default wallpaper encountered."); 283 c.setTileSource(null, null); 284 return; 285 } 286 287 c.setTileSource( 288 new DrawableTileSource(a, defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE), null); 289 c.setScale(1f); 290 c.setTouchEnabled(false); 291 a.setSystemWallpaperVisiblity(false); 292 } 293 @Override onSave(WallpaperPickerActivity a)294 public void onSave(WallpaperPickerActivity a) { 295 try { 296 WallpaperManager.getInstance(a).clear(); 297 a.setResult(RESULT_OK); 298 } catch (IOException e) { 299 Log.w("Setting wallpaper to default threw exception", e); 300 } 301 a.finish(); 302 } 303 @Override isSelectable()304 public boolean isSelectable() { 305 return true; 306 } 307 @Override isNamelessWallpaper()308 public boolean isNamelessWallpaper() { 309 return true; 310 } 311 } 312 setWallpaperStripYOffset(float offset)313 public void setWallpaperStripYOffset(float offset) { 314 mWallpaperStrip.setPadding(0, 0, 0, (int) offset); 315 } 316 317 /** 318 * shows the system wallpaper behind the window and hides the {@link 319 * #mCropView} if visible 320 * @param visible should the system wallpaper be shown 321 */ setSystemWallpaperVisiblity(final boolean visible)322 protected void setSystemWallpaperVisiblity(final boolean visible) { 323 // hide our own wallpaper preview if necessary 324 if(!visible) { 325 mCropView.setVisibility(View.VISIBLE); 326 } else { 327 changeWallpaperFlags(visible); 328 } 329 // the change of the flag must be delayed in order to avoid flickering, 330 // a simple post / double post does not suffice here 331 mCropView.postDelayed(new Runnable() { 332 @Override 333 public void run() { 334 if(!visible) { 335 changeWallpaperFlags(visible); 336 } else { 337 mCropView.setVisibility(View.INVISIBLE); 338 } 339 } 340 }, FLAG_POST_DELAY_MILLIS); 341 } 342 changeWallpaperFlags(boolean visible)343 private void changeWallpaperFlags(boolean visible) { 344 int desiredWallpaperFlag = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0; 345 int currentWallpaperFlag = getWindow().getAttributes().flags 346 & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; 347 if (desiredWallpaperFlag != currentWallpaperFlag) { 348 getWindow().setFlags(desiredWallpaperFlag, 349 WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER); 350 } 351 } 352 353 @Override setCropViewTileSource(BitmapSource bitmapSource, boolean touchEnabled, boolean moveToLeft, final Runnable postExecute)354 public void setCropViewTileSource(BitmapSource bitmapSource, 355 boolean touchEnabled, 356 boolean moveToLeft, 357 final Runnable postExecute) { 358 // we also want to show our own wallpaper instead of the one in the background 359 Runnable showPostExecuteRunnable = new Runnable() { 360 @Override 361 public void run() { 362 if(postExecute != null) { 363 postExecute.run(); 364 } 365 setSystemWallpaperVisiblity(false); 366 } 367 }; 368 super.setCropViewTileSource(bitmapSource, 369 touchEnabled, 370 moveToLeft, 371 showPostExecuteRunnable); 372 } 373 374 // called by onCreate; this is subclassed to overwrite WallpaperCropActivity init()375 protected void init() { 376 setContentView(R.layout.wallpaper_picker); 377 378 mCropView = (CropView) findViewById(R.id.cropView); 379 mCropView.setVisibility(View.INVISIBLE); 380 381 mWallpaperStrip = findViewById(R.id.wallpaper_strip); 382 mCropView.setTouchCallback(new CropView.TouchCallback() { 383 ViewPropertyAnimator mAnim; 384 @Override 385 public void onTouchDown() { 386 if (mAnim != null) { 387 mAnim.cancel(); 388 } 389 if (mWallpaperStrip.getAlpha() == 1f) { 390 mIgnoreNextTap = true; 391 } 392 mAnim = mWallpaperStrip.animate(); 393 mAnim.alpha(0f) 394 .setDuration(150) 395 .withEndAction(new Runnable() { 396 public void run() { 397 mWallpaperStrip.setVisibility(View.INVISIBLE); 398 } 399 }); 400 mAnim.setInterpolator(new AccelerateInterpolator(0.75f)); 401 mAnim.start(); 402 } 403 @Override 404 public void onTouchUp() { 405 mIgnoreNextTap = false; 406 } 407 @Override 408 public void onTap() { 409 boolean ignoreTap = mIgnoreNextTap; 410 mIgnoreNextTap = false; 411 if (!ignoreTap) { 412 if (mAnim != null) { 413 mAnim.cancel(); 414 } 415 mWallpaperStrip.setVisibility(View.VISIBLE); 416 mAnim = mWallpaperStrip.animate(); 417 mAnim.alpha(1f) 418 .setDuration(150) 419 .setInterpolator(new DecelerateInterpolator(0.75f)); 420 mAnim.start(); 421 } 422 } 423 }); 424 425 mThumbnailOnClickListener = new OnClickListener() { 426 public void onClick(View v) { 427 if (mActionMode != null) { 428 // When CAB is up, clicking toggles the item instead 429 if (v.isLongClickable()) { 430 mLongClickListener.onLongClick(v); 431 } 432 return; 433 } 434 mSetWallpaperButton.setEnabled(true); 435 WallpaperTileInfo info = (WallpaperTileInfo) v.getTag(); 436 if (info.isSelectable() && v.getVisibility() == View.VISIBLE) { 437 selectTile(v); 438 } 439 info.onClick(WallpaperPickerActivity.this); 440 } 441 }; 442 mLongClickListener = new View.OnLongClickListener() { 443 // Called when the user long-clicks on someView 444 public boolean onLongClick(View view) { 445 CheckableFrameLayout c = (CheckableFrameLayout) view; 446 c.toggle(); 447 448 if (mActionMode != null) { 449 mActionMode.invalidate(); 450 } else { 451 // Start the CAB using the ActionMode.Callback defined below 452 mActionMode = startActionMode(mActionModeCallback); 453 int childCount = mWallpapersView.getChildCount(); 454 for (int i = 0; i < childCount; i++) { 455 mWallpapersView.getChildAt(i).setSelected(false); 456 } 457 } 458 return true; 459 } 460 }; 461 462 // Populate the built-in wallpapers 463 ArrayList<WallpaperTileInfo> wallpapers = findBundledWallpapers(); 464 mWallpapersView = (LinearLayout) findViewById(R.id.wallpaper_list); 465 SimpleWallpapersAdapter ia = new SimpleWallpapersAdapter(this, wallpapers); 466 populateWallpapersFromAdapter(mWallpapersView, ia, false); 467 468 // Populate the saved wallpapers 469 mSavedImages = new SavedWallpaperImages(this); 470 mSavedImages.loadThumbnailsAndImageIdList(); 471 populateWallpapersFromAdapter(mWallpapersView, mSavedImages, true); 472 473 // Populate the live wallpapers 474 final LinearLayout liveWallpapersView = 475 (LinearLayout) findViewById(R.id.live_wallpaper_list); 476 final LiveWallpaperListAdapter a = new LiveWallpaperListAdapter(this); 477 a.registerDataSetObserver(new DataSetObserver() { 478 public void onChanged() { 479 liveWallpapersView.removeAllViews(); 480 populateWallpapersFromAdapter(liveWallpapersView, a, false); 481 initializeScrollForRtl(); 482 updateTileIndices(); 483 } 484 }); 485 486 // Populate the third-party wallpaper pickers 487 final LinearLayout thirdPartyWallpapersView = 488 (LinearLayout) findViewById(R.id.third_party_wallpaper_list); 489 final ThirdPartyWallpaperPickerListAdapter ta = 490 new ThirdPartyWallpaperPickerListAdapter(this); 491 populateWallpapersFromAdapter(thirdPartyWallpapersView, ta, false); 492 493 // Add a tile for the Gallery 494 LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list); 495 FrameLayout pickImageTile = (FrameLayout) getLayoutInflater(). 496 inflate(R.layout.wallpaper_picker_image_picker_item, masterWallpaperList, false); 497 setWallpaperItemPaddingToZero(pickImageTile); 498 masterWallpaperList.addView(pickImageTile, 0); 499 500 // Make its background the last photo taken on external storage 501 Bitmap lastPhoto = getThumbnailOfLastPhoto(); 502 if (lastPhoto != null) { 503 ImageView galleryThumbnailBg = 504 (ImageView) pickImageTile.findViewById(R.id.wallpaper_image); 505 galleryThumbnailBg.setImageBitmap(getThumbnailOfLastPhoto()); 506 int colorOverlay = getResources().getColor(R.color.wallpaper_picker_translucent_gray); 507 galleryThumbnailBg.setColorFilter(colorOverlay, PorterDuff.Mode.SRC_ATOP); 508 509 } 510 511 PickImageInfo pickImageInfo = new PickImageInfo(); 512 pickImageTile.setTag(pickImageInfo); 513 pickImageInfo.setView(pickImageTile); 514 pickImageTile.setOnClickListener(mThumbnailOnClickListener); 515 516 // Select the first item; wait for a layout pass so that we initialize the dimensions of 517 // cropView or the defaultWallpaperView first 518 mCropView.addOnLayoutChangeListener(new OnLayoutChangeListener() { 519 @Override 520 public void onLayoutChange(View v, int left, int top, int right, int bottom, 521 int oldLeft, int oldTop, int oldRight, int oldBottom) { 522 if ((right - left) > 0 && (bottom - top) > 0) { 523 if (mSelectedIndex >= 0 && mSelectedIndex < mWallpapersView.getChildCount()) { 524 mThumbnailOnClickListener.onClick( 525 mWallpapersView.getChildAt(mSelectedIndex)); 526 setSystemWallpaperVisiblity(false); 527 } 528 v.removeOnLayoutChangeListener(this); 529 } 530 } 531 }); 532 533 updateTileIndices(); 534 535 // Update the scroll for RTL 536 initializeScrollForRtl(); 537 538 // Create smooth layout transitions for when items are deleted 539 final LayoutTransition transitioner = new LayoutTransition(); 540 transitioner.setDuration(200); 541 transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0); 542 transitioner.setAnimator(LayoutTransition.DISAPPEARING, null); 543 mWallpapersView.setLayoutTransition(transitioner); 544 545 // Action bar 546 // Show the custom action bar view 547 final ActionBar actionBar = getActionBar(); 548 actionBar.setCustomView(R.layout.actionbar_set_wallpaper); 549 actionBar.getCustomView().setOnClickListener( 550 new View.OnClickListener() { 551 @Override 552 public void onClick(View v) { 553 if (mSelectedTile != null) { 554 WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag(); 555 info.onSave(WallpaperPickerActivity.this); 556 } else { 557 // no tile was selected, so we just finish the activity and go back 558 setResult(Activity.RESULT_OK); 559 finish(); 560 } 561 } 562 }); 563 mSetWallpaperButton = findViewById(R.id.set_wallpaper_button); 564 565 // CAB for deleting items 566 mActionModeCallback = new ActionMode.Callback() { 567 // Called when the action mode is created; startActionMode() was called 568 @Override 569 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 570 // Inflate a menu resource providing context menu items 571 MenuInflater inflater = mode.getMenuInflater(); 572 inflater.inflate(R.menu.cab_delete_wallpapers, menu); 573 return true; 574 } 575 576 private int numCheckedItems() { 577 int childCount = mWallpapersView.getChildCount(); 578 int numCheckedItems = 0; 579 for (int i = 0; i < childCount; i++) { 580 CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i); 581 if (c.isChecked()) { 582 numCheckedItems++; 583 } 584 } 585 return numCheckedItems; 586 } 587 588 // Called each time the action mode is shown. Always called after onCreateActionMode, 589 // but may be called multiple times if the mode is invalidated. 590 @Override 591 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 592 int numCheckedItems = numCheckedItems(); 593 if (numCheckedItems == 0) { 594 mode.finish(); 595 return true; 596 } else { 597 mode.setTitle(getResources().getQuantityString( 598 R.plurals.number_of_items_selected, numCheckedItems, numCheckedItems)); 599 return true; 600 } 601 } 602 603 // Called when the user selects a contextual menu item 604 @Override 605 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 606 int itemId = item.getItemId(); 607 if (itemId == R.id.menu_delete) { 608 int childCount = mWallpapersView.getChildCount(); 609 ArrayList<View> viewsToRemove = new ArrayList<View>(); 610 boolean selectedTileRemoved = false; 611 for (int i = 0; i < childCount; i++) { 612 CheckableFrameLayout c = 613 (CheckableFrameLayout) mWallpapersView.getChildAt(i); 614 if (c.isChecked()) { 615 WallpaperTileInfo info = (WallpaperTileInfo) c.getTag(); 616 info.onDelete(WallpaperPickerActivity.this); 617 viewsToRemove.add(c); 618 if (i == mSelectedIndex) { 619 selectedTileRemoved = true; 620 } 621 } 622 } 623 for (View v : viewsToRemove) { 624 mWallpapersView.removeView(v); 625 } 626 if (selectedTileRemoved) { 627 mSelectedIndex = -1; 628 mSelectedTile = null; 629 setSystemWallpaperVisiblity(true); 630 } 631 updateTileIndices(); 632 mode.finish(); // Action picked, so close the CAB 633 return true; 634 } else { 635 return false; 636 } 637 } 638 639 // Called when the user exits the action mode 640 @Override 641 public void onDestroyActionMode(ActionMode mode) { 642 int childCount = mWallpapersView.getChildCount(); 643 for (int i = 0; i < childCount; i++) { 644 CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i); 645 c.setChecked(false); 646 } 647 if (mSelectedTile != null) { 648 mSelectedTile.setSelected(true); 649 } 650 mActionMode = null; 651 } 652 }; 653 } 654 selectTile(View v)655 private void selectTile(View v) { 656 if (mSelectedTile != null) { 657 mSelectedTile.setSelected(false); 658 mSelectedTile = null; 659 } 660 mSelectedTile = v; 661 v.setSelected(true); 662 mSelectedIndex = mWallpapersView.indexOfChild(v); 663 // TODO: Remove this once the accessibility framework and 664 // services have better support for selection state. 665 v.announceForAccessibility( 666 getString(R.string.announce_selection, v.getContentDescription())); 667 } 668 initializeScrollForRtl()669 private void initializeScrollForRtl() { 670 final HorizontalScrollView scroll = 671 (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container); 672 673 if (scroll.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) { 674 final ViewTreeObserver observer = scroll.getViewTreeObserver(); 675 observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { 676 public void onGlobalLayout() { 677 LinearLayout masterWallpaperList = 678 (LinearLayout) findViewById(R.id.master_wallpaper_list); 679 scroll.scrollTo(masterWallpaperList.getWidth(), 0); 680 scroll.getViewTreeObserver().removeOnGlobalLayoutListener(this); 681 } 682 }); 683 } 684 } 685 getThumbnailOfLastPhoto()686 protected Bitmap getThumbnailOfLastPhoto() { 687 Cursor cursor = MediaStore.Images.Media.query(getContentResolver(), 688 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 689 new String[] { MediaStore.Images.ImageColumns._ID, 690 MediaStore.Images.ImageColumns.DATE_TAKEN}, 691 null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC LIMIT 1"); 692 693 Bitmap thumb = null; 694 if (cursor != null) { 695 if (cursor.moveToNext()) { 696 int id = cursor.getInt(0); 697 thumb = MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(), 698 id, MediaStore.Images.Thumbnails.MINI_KIND, null); 699 } 700 cursor.close(); 701 } 702 return thumb; 703 } 704 onStop()705 protected void onStop() { 706 super.onStop(); 707 mWallpaperStrip = findViewById(R.id.wallpaper_strip); 708 if (mWallpaperStrip.getAlpha() < 1f) { 709 mWallpaperStrip.setAlpha(1f); 710 mWallpaperStrip.setVisibility(View.VISIBLE); 711 } 712 } 713 onSaveInstanceState(Bundle outState)714 protected void onSaveInstanceState(Bundle outState) { 715 outState.putParcelableArrayList(TEMP_WALLPAPER_TILES, mTempWallpaperTiles); 716 outState.putInt(SELECTED_INDEX, mSelectedIndex); 717 } 718 onRestoreInstanceState(Bundle savedInstanceState)719 protected void onRestoreInstanceState(Bundle savedInstanceState) { 720 ArrayList<Uri> uris = savedInstanceState.getParcelableArrayList(TEMP_WALLPAPER_TILES); 721 for (Uri uri : uris) { 722 addTemporaryWallpaperTile(uri, true); 723 } 724 mSelectedIndex = savedInstanceState.getInt(SELECTED_INDEX, -1); 725 } 726 populateWallpapersFromAdapter(ViewGroup parent, BaseAdapter adapter, boolean addLongPressHandler)727 private void populateWallpapersFromAdapter(ViewGroup parent, BaseAdapter adapter, 728 boolean addLongPressHandler) { 729 for (int i = 0; i < adapter.getCount(); i++) { 730 FrameLayout thumbnail = (FrameLayout) adapter.getView(i, null, parent); 731 parent.addView(thumbnail, i); 732 WallpaperTileInfo info = (WallpaperTileInfo) adapter.getItem(i); 733 thumbnail.setTag(info); 734 info.setView(thumbnail); 735 if (addLongPressHandler) { 736 addLongPressHandler(thumbnail); 737 } 738 thumbnail.setOnClickListener(mThumbnailOnClickListener); 739 } 740 } 741 updateTileIndices()742 private void updateTileIndices() { 743 LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list); 744 final int childCount = masterWallpaperList.getChildCount(); 745 final Resources res = getResources(); 746 747 // Do two passes; the first pass gets the total number of tiles 748 int numTiles = 0; 749 for (int passNum = 0; passNum < 2; passNum++) { 750 int tileIndex = 0; 751 for (int i = 0; i < childCount; i++) { 752 View child = masterWallpaperList.getChildAt(i); 753 LinearLayout subList; 754 755 int subListStart; 756 int subListEnd; 757 if (child.getTag() instanceof WallpaperTileInfo) { 758 subList = masterWallpaperList; 759 subListStart = i; 760 subListEnd = i + 1; 761 } else { // if (child instanceof LinearLayout) { 762 subList = (LinearLayout) child; 763 subListStart = 0; 764 subListEnd = subList.getChildCount(); 765 } 766 767 for (int j = subListStart; j < subListEnd; j++) { 768 WallpaperTileInfo info = (WallpaperTileInfo) subList.getChildAt(j).getTag(); 769 if (info.isNamelessWallpaper()) { 770 if (passNum == 0) { 771 numTiles++; 772 } else { 773 CharSequence label = res.getString( 774 R.string.wallpaper_accessibility_name, ++tileIndex, numTiles); 775 info.onIndexUpdated(label); 776 } 777 } 778 } 779 } 780 } 781 } 782 getDefaultThumbnailSize(Resources res)783 private static Point getDefaultThumbnailSize(Resources res) { 784 return new Point(res.getDimensionPixelSize(R.dimen.wallpaperThumbnailWidth), 785 res.getDimensionPixelSize(R.dimen.wallpaperThumbnailHeight)); 786 787 } 788 createThumbnail(Point size, Context context, Uri uri, byte[] imageBytes, Resources res, int resId, int rotation, boolean leftAligned)789 private static Bitmap createThumbnail(Point size, Context context, Uri uri, byte[] imageBytes, 790 Resources res, int resId, int rotation, boolean leftAligned) { 791 int width = size.x; 792 int height = size.y; 793 794 BitmapCropTask cropTask; 795 if (uri != null) { 796 cropTask = new BitmapCropTask( 797 context, uri, null, rotation, width, height, false, true, null); 798 } else if (imageBytes != null) { 799 cropTask = new BitmapCropTask( 800 imageBytes, null, rotation, width, height, false, true, null); 801 } else { 802 cropTask = new BitmapCropTask( 803 context, res, resId, null, rotation, width, height, false, true, null); 804 } 805 Point bounds = cropTask.getImageBounds(); 806 if (bounds == null || bounds.x == 0 || bounds.y == 0) { 807 return null; 808 } 809 810 Matrix rotateMatrix = new Matrix(); 811 rotateMatrix.setRotate(rotation); 812 float[] rotatedBounds = new float[] { bounds.x, bounds.y }; 813 rotateMatrix.mapPoints(rotatedBounds); 814 rotatedBounds[0] = Math.abs(rotatedBounds[0]); 815 rotatedBounds[1] = Math.abs(rotatedBounds[1]); 816 817 RectF cropRect = WallpaperCropActivity.getMaxCropRect( 818 (int) rotatedBounds[0], (int) rotatedBounds[1], width, height, leftAligned); 819 cropTask.setCropBounds(cropRect); 820 821 if (cropTask.cropBitmap()) { 822 return cropTask.getCroppedBitmap(); 823 } else { 824 return null; 825 } 826 } 827 addTemporaryWallpaperTile(final Uri uri, boolean fromRestore)828 private void addTemporaryWallpaperTile(final Uri uri, boolean fromRestore) { 829 mTempWallpaperTiles.add(uri); 830 // Add a tile for the image picked from Gallery 831 final FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater(). 832 inflate(R.layout.wallpaper_picker_item, mWallpapersView, false); 833 pickedImageThumbnail.setVisibility(View.GONE); 834 setWallpaperItemPaddingToZero(pickedImageThumbnail); 835 mWallpapersView.addView(pickedImageThumbnail, 0); 836 837 // Load the thumbnail 838 final ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image); 839 final Point defaultSize = getDefaultThumbnailSize(this.getResources()); 840 final Context context = this; 841 new AsyncTask<Void, Bitmap, Bitmap>() { 842 protected Bitmap doInBackground(Void...args) { 843 try { 844 int rotation = WallpaperCropActivity.getRotationFromExif(context, uri); 845 return createThumbnail(defaultSize, context, uri, null, null, 0, rotation, false); 846 } catch (SecurityException securityException) { 847 if (isDestroyed()) { 848 // Temporarily granted permissions are revoked when the activity 849 // finishes, potentially resulting in a SecurityException here. 850 // Even though {@link #isDestroyed} might also return true in different 851 // situations where the configuration changes, we are fine with 852 // catching these cases here as well. 853 cancel(false); 854 } else { 855 // otherwise it had a different cause and we throw it further 856 throw securityException; 857 } 858 return null; 859 } 860 } 861 protected void onPostExecute(Bitmap thumb) { 862 if (!isCancelled() && thumb != null) { 863 image.setImageBitmap(thumb); 864 Drawable thumbDrawable = image.getDrawable(); 865 thumbDrawable.setDither(true); 866 pickedImageThumbnail.setVisibility(View.VISIBLE); 867 } else { 868 Log.e(TAG, "Error loading thumbnail for uri=" + uri); 869 } 870 } 871 }.execute(); 872 873 UriWallpaperInfo info = new UriWallpaperInfo(uri); 874 pickedImageThumbnail.setTag(info); 875 info.setView(pickedImageThumbnail); 876 addLongPressHandler(pickedImageThumbnail); 877 updateTileIndices(); 878 pickedImageThumbnail.setOnClickListener(mThumbnailOnClickListener); 879 if (!fromRestore) { 880 mThumbnailOnClickListener.onClick(pickedImageThumbnail); 881 } 882 } 883 onActivityResult(int requestCode, int resultCode, Intent data)884 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 885 if (requestCode == IMAGE_PICK && resultCode == RESULT_OK) { 886 if (data != null && data.getData() != null) { 887 Uri uri = data.getData(); 888 addTemporaryWallpaperTile(uri, false); 889 } 890 } else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY) { 891 setResult(RESULT_OK); 892 finish(); 893 } else if (requestCode == PICK_LIVE_WALLPAPER) { 894 WallpaperManager wm = WallpaperManager.getInstance(this); 895 final WallpaperInfo oldLiveWallpaper = mLiveWallpaperInfoOnPickerLaunch; 896 final WallpaperInfo clickedWallpaper = mLastClickedLiveWallpaperInfo; 897 WallpaperInfo newLiveWallpaper = wm.getWallpaperInfo(); 898 // Try to figure out if a live wallpaper was set; 899 if (newLiveWallpaper != null && 900 (oldLiveWallpaper == null 901 || !oldLiveWallpaper.getComponent() 902 .equals(newLiveWallpaper.getComponent()) 903 || clickedWallpaper.getComponent() 904 .equals(oldLiveWallpaper.getComponent()))) { 905 // Return if a live wallpaper was set 906 setResult(RESULT_OK); 907 finish(); 908 } 909 } 910 } 911 setWallpaperItemPaddingToZero(FrameLayout frameLayout)912 static void setWallpaperItemPaddingToZero(FrameLayout frameLayout) { 913 frameLayout.setPadding(0, 0, 0, 0); 914 frameLayout.setForeground(new ZeroPaddingDrawable(frameLayout.getForeground())); 915 } 916 addLongPressHandler(View v)917 private void addLongPressHandler(View v) { 918 v.setOnLongClickListener(mLongClickListener); 919 } 920 findBundledWallpapers()921 private ArrayList<WallpaperTileInfo> findBundledWallpapers() { 922 final PackageManager pm = getPackageManager(); 923 final ArrayList<WallpaperTileInfo> bundled = new ArrayList<WallpaperTileInfo>(24); 924 925 Partner partner = Partner.get(pm); 926 if (partner != null) { 927 final Resources partnerRes = partner.getResources(); 928 final int resId = partnerRes.getIdentifier(Partner.RES_WALLPAPERS, "array", 929 partner.getPackageName()); 930 if (resId != 0) { 931 addWallpapers(bundled, partnerRes, partner.getPackageName(), resId); 932 } 933 934 // Add system wallpapers 935 File systemDir = partner.getWallpaperDirectory(); 936 if (systemDir != null && systemDir.isDirectory()) { 937 for (File file : systemDir.listFiles()) { 938 if (!file.isFile()) { 939 continue; 940 } 941 String name = file.getName(); 942 int dotPos = name.lastIndexOf('.'); 943 String extension = ""; 944 if (dotPos >= -1) { 945 extension = name.substring(dotPos); 946 name = name.substring(0, dotPos); 947 } 948 949 if (name.endsWith("_small")) { 950 // it is a thumbnail 951 continue; 952 } 953 954 File thumbnail = new File(systemDir, name + "_small" + extension); 955 Bitmap thumb = BitmapFactory.decodeFile(thumbnail.getAbsolutePath()); 956 if (thumb != null) { 957 bundled.add(new FileWallpaperInfo(file, new BitmapDrawable(thumb))); 958 } 959 } 960 } 961 } 962 963 Pair<ApplicationInfo, Integer> r = getWallpaperArrayResourceId(); 964 if (r != null) { 965 try { 966 Resources wallpaperRes = getPackageManager().getResourcesForApplication(r.first); 967 addWallpapers(bundled, wallpaperRes, r.first.packageName, r.second); 968 } catch (PackageManager.NameNotFoundException e) { 969 } 970 } 971 972 if (partner == null || !partner.hideDefaultWallpaper()) { 973 // Add an entry for the default wallpaper (stored in system resources) 974 WallpaperTileInfo defaultWallpaperInfo = 975 (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) 976 ? getPreKKDefaultWallpaperInfo() 977 : getDefaultWallpaper(); 978 if (defaultWallpaperInfo != null) { 979 bundled.add(0, defaultWallpaperInfo); 980 } 981 } 982 return bundled; 983 } 984 writeImageToFileAsJpeg(File f, Bitmap b)985 private boolean writeImageToFileAsJpeg(File f, Bitmap b) { 986 try { 987 f.createNewFile(); 988 FileOutputStream thumbFileStream = 989 openFileOutput(f.getName(), Context.MODE_PRIVATE); 990 b.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream); 991 thumbFileStream.close(); 992 return true; 993 } catch (IOException e) { 994 Log.e(TAG, "Error while writing bitmap to file " + e); 995 f.delete(); 996 } 997 return false; 998 } 999 getDefaultThumbFile()1000 private File getDefaultThumbFile() { 1001 return new File(getFilesDir(), Build.VERSION.SDK_INT 1002 + "_" + DEFAULT_WALLPAPER_THUMBNAIL_FILENAME); 1003 } 1004 saveDefaultWallpaperThumb(Bitmap b)1005 private boolean saveDefaultWallpaperThumb(Bitmap b) { 1006 // Delete old thumbnails. 1007 new File(getFilesDir(), OLD_DEFAULT_WALLPAPER_THUMBNAIL_FILENAME).delete(); 1008 new File(getFilesDir(), DEFAULT_WALLPAPER_THUMBNAIL_FILENAME).delete(); 1009 1010 for (int i = Build.VERSION_CODES.JELLY_BEAN; i < Build.VERSION.SDK_INT; i++) { 1011 new File(getFilesDir(), i + "_" + DEFAULT_WALLPAPER_THUMBNAIL_FILENAME).delete(); 1012 } 1013 return writeImageToFileAsJpeg(getDefaultThumbFile(), b); 1014 } 1015 getPreKKDefaultWallpaperInfo()1016 private ResourceWallpaperInfo getPreKKDefaultWallpaperInfo() { 1017 Resources sysRes = Resources.getSystem(); 1018 int resId = sysRes.getIdentifier("default_wallpaper", "drawable", "android"); 1019 1020 File defaultThumbFile = getDefaultThumbFile(); 1021 Bitmap thumb = null; 1022 boolean defaultWallpaperExists = false; 1023 if (defaultThumbFile.exists()) { 1024 thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath()); 1025 defaultWallpaperExists = true; 1026 } else { 1027 Resources res = getResources(); 1028 Point defaultThumbSize = getDefaultThumbnailSize(res); 1029 int rotation = WallpaperCropActivity.getRotationFromExif(res, resId); 1030 thumb = createThumbnail( 1031 defaultThumbSize, this, null, null, sysRes, resId, rotation, false); 1032 if (thumb != null) { 1033 defaultWallpaperExists = saveDefaultWallpaperThumb(thumb); 1034 } 1035 } 1036 if (defaultWallpaperExists) { 1037 return new ResourceWallpaperInfo(sysRes, resId, new BitmapDrawable(thumb)); 1038 } 1039 return null; 1040 } 1041 1042 @TargetApi(Build.VERSION_CODES.KITKAT) getDefaultWallpaper()1043 private DefaultWallpaperInfo getDefaultWallpaper() { 1044 File defaultThumbFile = getDefaultThumbFile(); 1045 Bitmap thumb = null; 1046 boolean defaultWallpaperExists = false; 1047 if (defaultThumbFile.exists()) { 1048 thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath()); 1049 defaultWallpaperExists = true; 1050 } else { 1051 Resources res = getResources(); 1052 Point defaultThumbSize = getDefaultThumbnailSize(res); 1053 Drawable wallpaperDrawable = WallpaperManager.getInstance(this).getBuiltInDrawable( 1054 defaultThumbSize.x, defaultThumbSize.y, true, 0.5f, 0.5f); 1055 if (wallpaperDrawable != null) { 1056 thumb = Bitmap.createBitmap( 1057 defaultThumbSize.x, defaultThumbSize.y, Bitmap.Config.ARGB_8888); 1058 Canvas c = new Canvas(thumb); 1059 wallpaperDrawable.setBounds(0, 0, defaultThumbSize.x, defaultThumbSize.y); 1060 wallpaperDrawable.draw(c); 1061 c.setBitmap(null); 1062 } 1063 if (thumb != null) { 1064 defaultWallpaperExists = saveDefaultWallpaperThumb(thumb); 1065 } 1066 } 1067 if (defaultWallpaperExists) { 1068 return new DefaultWallpaperInfo(new BitmapDrawable(thumb)); 1069 } 1070 return null; 1071 } 1072 getWallpaperArrayResourceId()1073 public Pair<ApplicationInfo, Integer> getWallpaperArrayResourceId() { 1074 // Context.getPackageName() may return the "original" package name, 1075 // com.android.launcher3; Resources needs the real package name, 1076 // com.android.launcher3. So we ask Resources for what it thinks the 1077 // package name should be. 1078 final String packageName = getResources().getResourcePackageName(R.array.wallpapers); 1079 try { 1080 ApplicationInfo info = getPackageManager().getApplicationInfo(packageName, 0); 1081 return new Pair<ApplicationInfo, Integer>(info, R.array.wallpapers); 1082 } catch (PackageManager.NameNotFoundException e) { 1083 return null; 1084 } 1085 } 1086 addWallpapers(ArrayList<WallpaperTileInfo> known, Resources res, String packageName, int listResId)1087 private void addWallpapers(ArrayList<WallpaperTileInfo> known, Resources res, 1088 String packageName, int listResId) { 1089 final String[] extras = res.getStringArray(listResId); 1090 for (String extra : extras) { 1091 int resId = res.getIdentifier(extra, "drawable", packageName); 1092 if (resId != 0) { 1093 final int thumbRes = res.getIdentifier(extra + "_small", "drawable", packageName); 1094 1095 if (thumbRes != 0) { 1096 ResourceWallpaperInfo wallpaperInfo = 1097 new ResourceWallpaperInfo(res, resId, res.getDrawable(thumbRes)); 1098 known.add(wallpaperInfo); 1099 // Log.d(TAG, "add: [" + packageName + "]: " + extra + " (" + res + ")"); 1100 } 1101 } else { 1102 Log.e(TAG, "Couldn't find wallpaper " + extra); 1103 } 1104 } 1105 } 1106 getCropView()1107 public CropView getCropView() { 1108 return mCropView; 1109 } 1110 getSavedImages()1111 public SavedWallpaperImages getSavedImages() { 1112 return mSavedImages; 1113 } 1114 onLiveWallpaperPickerLaunch(WallpaperInfo info)1115 public void onLiveWallpaperPickerLaunch(WallpaperInfo info) { 1116 mLastClickedLiveWallpaperInfo = info; 1117 mLiveWallpaperInfoOnPickerLaunch = WallpaperManager.getInstance(this).getWallpaperInfo(); 1118 } 1119 1120 static class ZeroPaddingDrawable extends LevelListDrawable { ZeroPaddingDrawable(Drawable d)1121 public ZeroPaddingDrawable(Drawable d) { 1122 super(); 1123 addLevel(0, 0, d); 1124 setLevel(0); 1125 } 1126 1127 @Override getPadding(Rect padding)1128 public boolean getPadding(Rect padding) { 1129 padding.set(0, 0, 0, 0); 1130 return true; 1131 } 1132 } 1133 1134 private static class SimpleWallpapersAdapter extends ArrayAdapter<WallpaperTileInfo> { 1135 private final LayoutInflater mLayoutInflater; 1136 SimpleWallpapersAdapter(Activity activity, ArrayList<WallpaperTileInfo> wallpapers)1137 SimpleWallpapersAdapter(Activity activity, ArrayList<WallpaperTileInfo> wallpapers) { 1138 super(activity, R.layout.wallpaper_picker_item, wallpapers); 1139 mLayoutInflater = activity.getLayoutInflater(); 1140 } 1141 getView(int position, View convertView, ViewGroup parent)1142 public View getView(int position, View convertView, ViewGroup parent) { 1143 Drawable thumb = getItem(position).mThumb; 1144 if (thumb == null) { 1145 Log.e(TAG, "Error decoding thumbnail for wallpaper #" + position); 1146 } 1147 return createImageTileView(mLayoutInflater, convertView, parent, thumb); 1148 } 1149 } 1150 createImageTileView(LayoutInflater layoutInflater, View convertView, ViewGroup parent, Drawable thumb)1151 public static View createImageTileView(LayoutInflater layoutInflater, 1152 View convertView, ViewGroup parent, Drawable thumb) { 1153 View view; 1154 1155 if (convertView == null) { 1156 view = layoutInflater.inflate(R.layout.wallpaper_picker_item, parent, false); 1157 } else { 1158 view = convertView; 1159 } 1160 1161 setWallpaperItemPaddingToZero((FrameLayout) view); 1162 1163 ImageView image = (ImageView) view.findViewById(R.id.wallpaper_image); 1164 1165 if (thumb != null) { 1166 image.setImageDrawable(thumb); 1167 thumb.setDither(true); 1168 } 1169 1170 return view; 1171 } 1172 1173 // In Launcher3, we override this with a method that catches exceptions 1174 // from starting activities; didn't want to copy and paste code into here startActivityForResultSafely(Intent intent, int requestCode)1175 public void startActivityForResultSafely(Intent intent, int requestCode) { 1176 startActivityForResult(intent, requestCode); 1177 } 1178 } 1179