Home | History | Annotate | Download | only in launcher3
      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.Manifest.permission;
     20 import android.animation.LayoutTransition;
     21 import android.annotation.TargetApi;
     22 import android.app.ActionBar;
     23 import android.app.Activity;
     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.RectF;
     39 import android.graphics.drawable.BitmapDrawable;
     40 import android.graphics.drawable.Drawable;
     41 import android.Manifest;
     42 import android.net.Uri;
     43 import android.os.AsyncTask;
     44 import android.os.Build;
     45 import android.os.Bundle;
     46 import android.os.Process;
     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.MotionEvent;
     56 import android.view.View;
     57 import android.view.View.OnClickListener;
     58 import android.view.View.OnLayoutChangeListener;
     59 import android.view.ViewGroup;
     60 import android.view.ViewPropertyAnimator;
     61 import android.view.ViewTreeObserver;
     62 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
     63 import android.view.WindowManager;
     64 import android.view.animation.AccelerateInterpolator;
     65 import android.view.animation.DecelerateInterpolator;
     66 import android.widget.ArrayAdapter;
     67 import android.widget.BaseAdapter;
     68 import android.widget.FrameLayout;
     69 import android.widget.HorizontalScrollView;
     70 import android.widget.ImageView;
     71 import android.widget.LinearLayout;
     72 import android.widget.Toast;
     73 
     74 import com.android.gallery3d.common.BitmapCropTask;
     75 import com.android.gallery3d.common.BitmapUtils;
     76 import com.android.gallery3d.common.Utils;
     77 import com.android.launcher3.util.Thunk;
     78 import com.android.launcher3.util.WallpaperUtils;
     79 import com.android.photos.BitmapRegionTileSource;
     80 import com.android.photos.BitmapRegionTileSource.BitmapSource;
     81 import com.android.photos.views.TiledImageRenderer.TileSource;
     82 
     83 import java.io.File;
     84 import java.io.FileOutputStream;
     85 import java.io.IOException;
     86 import java.util.ArrayList;
     87 
     88 public class WallpaperPickerActivity extends WallpaperCropActivity {
     89     static final String TAG = "Launcher.WallpaperPickerActivity";
     90 
     91     public static final int IMAGE_PICK = 5;
     92     public static final int PICK_WALLPAPER_THIRD_PARTY_ACTIVITY = 6;
     93     private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES";
     94     private static final String SELECTED_INDEX = "SELECTED_INDEX";
     95     private static final int FLAG_POST_DELAY_MILLIS = 200;
     96 
     97     @Thunk View mSelectedTile;
     98     @Thunk boolean mIgnoreNextTap;
     99     @Thunk OnClickListener mThumbnailOnClickListener;
    100 
    101     @Thunk LinearLayout mWallpapersView;
    102     @Thunk HorizontalScrollView mWallpaperScrollContainer;
    103 
    104     @Thunk ActionMode.Callback mActionModeCallback;
    105     @Thunk ActionMode mActionMode;
    106 
    107     @Thunk View.OnLongClickListener mLongClickListener;
    108 
    109     ArrayList<Uri> mTempWallpaperTiles = new ArrayList<Uri>();
    110     private SavedWallpaperImages mSavedImages;
    111     @Thunk int mSelectedIndex = -1;
    112 
    113     public static abstract class WallpaperTileInfo {
    114         protected View mView;
    115         public Drawable mThumb;
    116 
    117         public void setView(View v) {
    118             mView = v;
    119         }
    120         public void onClick(WallpaperPickerActivity a) {}
    121         public void onSave(WallpaperPickerActivity a) {}
    122         public void onDelete(WallpaperPickerActivity a) {}
    123         public boolean isSelectable() { return false; }
    124         public boolean isNamelessWallpaper() { return false; }
    125         public void onIndexUpdated(CharSequence label) {
    126             if (isNamelessWallpaper()) {
    127                 mView.setContentDescription(label);
    128             }
    129         }
    130     }
    131 
    132     public static class PickImageInfo extends WallpaperTileInfo {
    133         @Override
    134         public void onClick(WallpaperPickerActivity a) {
    135             Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    136             intent.setType("image/*");
    137             a.startActivityForResultSafely(intent, IMAGE_PICK);
    138         }
    139     }
    140 
    141     public static class UriWallpaperInfo extends WallpaperTileInfo {
    142         private Uri mUri;
    143         public UriWallpaperInfo(Uri uri) {
    144             mUri = uri;
    145         }
    146         @Override
    147         public void onClick(final WallpaperPickerActivity a) {
    148             a.setWallpaperButtonEnabled(false);
    149             final BitmapRegionTileSource.UriBitmapSource bitmapSource =
    150                     new BitmapRegionTileSource.UriBitmapSource(a.getContext(), mUri);
    151             a.setCropViewTileSource(bitmapSource, true, false, null, new Runnable() {
    152 
    153                 @Override
    154                 public void run() {
    155                     if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
    156                         a.selectTile(mView);
    157                         a.setWallpaperButtonEnabled(true);
    158                     } else {
    159                         ViewGroup parent = (ViewGroup) mView.getParent();
    160                         if (parent != null) {
    161                             parent.removeView(mView);
    162                             Toast.makeText(a.getContext(), R.string.image_load_fail,
    163                                     Toast.LENGTH_SHORT).show();
    164                         }
    165                     }
    166                 }
    167             });
    168         }
    169         @Override
    170         public void onSave(final WallpaperPickerActivity a) {
    171             boolean finishActivityWhenDone = true;
    172             BitmapCropTask.OnBitmapCroppedHandler h = new BitmapCropTask.OnBitmapCroppedHandler() {
    173                 public void onBitmapCropped(byte[] imageBytes) {
    174                     Point thumbSize = getDefaultThumbnailSize(a.getResources());
    175                     // rotation is set to 0 since imageBytes has already been correctly rotated
    176                     Bitmap thumb = createThumbnail(
    177                             thumbSize, null, null, imageBytes, null, 0, 0, true);
    178                     a.getSavedImages().writeImage(thumb, imageBytes);
    179                 }
    180             };
    181             a.cropImageAndSetWallpaper(mUri, h, finishActivityWhenDone);
    182         }
    183         @Override
    184         public boolean isSelectable() {
    185             return true;
    186         }
    187         @Override
    188         public boolean isNamelessWallpaper() {
    189             return true;
    190         }
    191     }
    192 
    193     public static class FileWallpaperInfo extends WallpaperTileInfo {
    194         private File mFile;
    195 
    196         public FileWallpaperInfo(File target, Drawable thumb) {
    197             mFile = target;
    198             mThumb = thumb;
    199         }
    200         @Override
    201         public void onClick(final WallpaperPickerActivity a) {
    202             a.setWallpaperButtonEnabled(false);
    203             final BitmapRegionTileSource.UriBitmapSource bitmapSource =
    204                     new BitmapRegionTileSource.UriBitmapSource(a.getContext(), Uri.fromFile(mFile));
    205             a.setCropViewTileSource(bitmapSource, false, true, null, new Runnable() {
    206 
    207                 @Override
    208                 public void run() {
    209                     if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
    210                         a.setWallpaperButtonEnabled(true);
    211                     }
    212                 }
    213             });
    214         }
    215         @Override
    216         public void onSave(WallpaperPickerActivity a) {
    217             a.setWallpaper(Uri.fromFile(mFile), true);
    218         }
    219         @Override
    220         public boolean isSelectable() {
    221             return true;
    222         }
    223         @Override
    224         public boolean isNamelessWallpaper() {
    225             return true;
    226         }
    227     }
    228 
    229     public static class ResourceWallpaperInfo extends WallpaperTileInfo {
    230         private Resources mResources;
    231         private int mResId;
    232 
    233         public ResourceWallpaperInfo(Resources res, int resId, Drawable thumb) {
    234             mResources = res;
    235             mResId = resId;
    236             mThumb = thumb;
    237         }
    238         @Override
    239         public void onClick(final WallpaperPickerActivity a) {
    240             a.setWallpaperButtonEnabled(false);
    241             final BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
    242                     new BitmapRegionTileSource.ResourceBitmapSource(mResources, mResId);
    243             a.setCropViewTileSource(bitmapSource, false, false, new CropViewScaleProvider() {
    244 
    245                 @Override
    246                 public float getScale(TileSource src) {
    247                     Point wallpaperSize = WallpaperUtils.getDefaultWallpaperSize(
    248                             a.getResources(), a.getWindowManager());
    249                     RectF crop = Utils.getMaxCropRect(
    250                             src.getImageWidth(), src.getImageHeight(),
    251                             wallpaperSize.x, wallpaperSize.y, false);
    252                     return wallpaperSize.x / crop.width();
    253                 }
    254             }, new Runnable() {
    255 
    256                 @Override
    257                 public void run() {
    258                     if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
    259                         a.setWallpaperButtonEnabled(true);
    260                     }
    261                 }
    262             });
    263         }
    264         @Override
    265         public void onSave(WallpaperPickerActivity a) {
    266             boolean finishActivityWhenDone = true;
    267             a.cropImageAndSetWallpaper(mResources, mResId, finishActivityWhenDone);
    268         }
    269         @Override
    270         public boolean isSelectable() {
    271             return true;
    272         }
    273         @Override
    274         public boolean isNamelessWallpaper() {
    275             return true;
    276         }
    277     }
    278 
    279     @TargetApi(Build.VERSION_CODES.KITKAT)
    280     public static class DefaultWallpaperInfo extends WallpaperTileInfo {
    281         public DefaultWallpaperInfo(Drawable thumb) {
    282             mThumb = thumb;
    283         }
    284         @Override
    285         public void onClick(WallpaperPickerActivity a) {
    286             CropView c = a.getCropView();
    287             Drawable defaultWallpaper = WallpaperManager.getInstance(a.getContext())
    288                     .getBuiltInDrawable(c.getWidth(), c.getHeight(), false, 0.5f, 0.5f);
    289             if (defaultWallpaper == null) {
    290                 Log.w(TAG, "Null default wallpaper encountered.");
    291                 c.setTileSource(null, null);
    292                 return;
    293             }
    294 
    295             LoadRequest req = new LoadRequest();
    296             req.moveToLeft = false;
    297             req.touchEnabled = false;
    298             req.scaleProvider = new CropViewScaleProvider() {
    299 
    300                 @Override
    301                 public float getScale(TileSource src) {
    302                     return 1f;
    303                 }
    304             };
    305             req.result = new DrawableTileSource(a.getContext(),
    306                     defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE);
    307             a.onLoadRequestComplete(req, true);
    308         }
    309         @Override
    310         public void onSave(WallpaperPickerActivity a) {
    311             try {
    312                 WallpaperManager.getInstance(a.getContext()).clear();
    313                 a.setResult(Activity.RESULT_OK);
    314             } catch (IOException e) {
    315                 Log.w("Setting wallpaper to default threw exception", e);
    316             }
    317             a.finish();
    318         }
    319         @Override
    320         public boolean isSelectable() {
    321             return true;
    322         }
    323         @Override
    324         public boolean isNamelessWallpaper() {
    325             return true;
    326         }
    327     }
    328 
    329     /**
    330      * shows the system wallpaper behind the window and hides the {@link
    331      * #mCropView} if visible
    332      * @param visible should the system wallpaper be shown
    333      */
    334     protected void setSystemWallpaperVisiblity(final boolean visible) {
    335         // hide our own wallpaper preview if necessary
    336         if(!visible) {
    337             mCropView.setVisibility(View.VISIBLE);
    338         } else {
    339             changeWallpaperFlags(visible);
    340         }
    341         // the change of the flag must be delayed in order to avoid flickering,
    342         // a simple post / double post does not suffice here
    343         mCropView.postDelayed(new Runnable() {
    344             @Override
    345             public void run() {
    346                 if(!visible) {
    347                     changeWallpaperFlags(visible);
    348                 } else {
    349                     mCropView.setVisibility(View.INVISIBLE);
    350                 }
    351             }
    352         }, FLAG_POST_DELAY_MILLIS);
    353     }
    354 
    355     @Thunk void changeWallpaperFlags(boolean visible) {
    356         int desiredWallpaperFlag = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
    357         int currentWallpaperFlag = getWindow().getAttributes().flags
    358                 & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
    359         if (desiredWallpaperFlag != currentWallpaperFlag) {
    360             getWindow().setFlags(desiredWallpaperFlag,
    361                     WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
    362         }
    363     }
    364 
    365     @Override
    366     protected void onLoadRequestComplete(LoadRequest req, boolean success) {
    367         super.onLoadRequestComplete(req, success);
    368         if (success) {
    369             setSystemWallpaperVisiblity(false);
    370         }
    371     }
    372 
    373     // called by onCreate; this is subclassed to overwrite WallpaperCropActivity
    374     protected void init() {
    375         setContentView(R.layout.wallpaper_picker);
    376 
    377         mCropView = (CropView) findViewById(R.id.cropView);
    378         mCropView.setVisibility(View.INVISIBLE);
    379 
    380         mProgressView = findViewById(R.id.loading);
    381         mWallpaperScrollContainer = (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container);
    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 (mWallpaperScrollContainer.getAlpha() == 1f) {
    390                     mIgnoreNextTap = true;
    391                 }
    392                 mAnim = mWallpaperScrollContainer.animate();
    393                 mAnim.alpha(0f)
    394                     .setDuration(150)
    395                     .withEndAction(new Runnable() {
    396                         public void run() {
    397                             mWallpaperScrollContainer.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                     mWallpaperScrollContainer.setVisibility(View.VISIBLE);
    416                     mAnim = mWallpaperScrollContainer.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                 setWallpaperButtonEnabled(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(getContext(), wallpapers);
    466         populateWallpapersFromAdapter(mWallpapersView, ia, false);
    467 
    468         // Populate the saved wallpapers
    469         mSavedImages = new SavedWallpaperImages(getContext());
    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(getContext());
    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(getContext());
    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         masterWallpaperList.addView(pickImageTile, 0);
    498 
    499         // Make its background the last photo taken on external storage
    500         Bitmap lastPhoto = getThumbnailOfLastPhoto();
    501         if (lastPhoto != null) {
    502             ImageView galleryThumbnailBg =
    503                     (ImageView) pickImageTile.findViewById(R.id.wallpaper_image);
    504             galleryThumbnailBg.setImageBitmap(lastPhoto);
    505             int colorOverlay = getResources().getColor(R.color.wallpaper_picker_translucent_gray);
    506             galleryThumbnailBg.setColorFilter(colorOverlay, PorterDuff.Mode.SRC_ATOP);
    507         }
    508 
    509         PickImageInfo pickImageInfo = new PickImageInfo();
    510         pickImageTile.setTag(pickImageInfo);
    511         pickImageInfo.setView(pickImageTile);
    512         pickImageTile.setOnClickListener(mThumbnailOnClickListener);
    513 
    514         // Select the first item; wait for a layout pass so that we initialize the dimensions of
    515         // cropView or the defaultWallpaperView first
    516         mCropView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
    517             @Override
    518             public void onLayoutChange(View v, int left, int top, int right, int bottom,
    519                     int oldLeft, int oldTop, int oldRight, int oldBottom) {
    520                 if ((right - left) > 0 && (bottom - top) > 0) {
    521                     if (mSelectedIndex >= 0 && mSelectedIndex < mWallpapersView.getChildCount()) {
    522                         mThumbnailOnClickListener.onClick(
    523                                 mWallpapersView.getChildAt(mSelectedIndex));
    524                         setSystemWallpaperVisiblity(false);
    525                     }
    526                     v.removeOnLayoutChangeListener(this);
    527                 }
    528             }
    529         });
    530 
    531         updateTileIndices();
    532 
    533         // Update the scroll for RTL
    534         initializeScrollForRtl();
    535 
    536         // Create smooth layout transitions for when items are deleted
    537         final LayoutTransition transitioner = new LayoutTransition();
    538         transitioner.setDuration(200);
    539         transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
    540         transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
    541         mWallpapersView.setLayoutTransition(transitioner);
    542 
    543         // Action bar
    544         // Show the custom action bar view
    545         final ActionBar actionBar = getActionBar();
    546         actionBar.setCustomView(R.layout.actionbar_set_wallpaper);
    547         actionBar.getCustomView().setOnClickListener(
    548                 new View.OnClickListener() {
    549                     @Override
    550                     public void onClick(View v) {
    551                         if (mSelectedTile != null) {
    552                             WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag();
    553                             info.onSave(WallpaperPickerActivity.this);
    554                         } else {
    555                             // no tile was selected, so we just finish the activity and go back
    556                             setResult(Activity.RESULT_OK);
    557                             finish();
    558                         }
    559                     }
    560                 });
    561         mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
    562 
    563         // CAB for deleting items
    564         mActionModeCallback = new ActionMode.Callback() {
    565             // Called when the action mode is created; startActionMode() was called
    566             @Override
    567             public boolean onCreateActionMode(ActionMode mode, Menu menu) {
    568                 // Inflate a menu resource providing context menu items
    569                 MenuInflater inflater = mode.getMenuInflater();
    570                 inflater.inflate(R.menu.cab_delete_wallpapers, menu);
    571                 return true;
    572             }
    573 
    574             private int numCheckedItems() {
    575                 int childCount = mWallpapersView.getChildCount();
    576                 int numCheckedItems = 0;
    577                 for (int i = 0; i < childCount; i++) {
    578                     CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
    579                     if (c.isChecked()) {
    580                         numCheckedItems++;
    581                     }
    582                 }
    583                 return numCheckedItems;
    584             }
    585 
    586             // Called each time the action mode is shown. Always called after onCreateActionMode,
    587             // but may be called multiple times if the mode is invalidated.
    588             @Override
    589             public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
    590                 int numCheckedItems = numCheckedItems();
    591                 if (numCheckedItems == 0) {
    592                     mode.finish();
    593                     return true;
    594                 } else {
    595                     mode.setTitle(getResources().getQuantityString(
    596                             R.plurals.number_of_items_selected, numCheckedItems, numCheckedItems));
    597                     return true;
    598                 }
    599             }
    600 
    601             // Called when the user selects a contextual menu item
    602             @Override
    603             public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
    604                 int itemId = item.getItemId();
    605                 if (itemId == R.id.menu_delete) {
    606                     int childCount = mWallpapersView.getChildCount();
    607                     ArrayList<View> viewsToRemove = new ArrayList<View>();
    608                     boolean selectedTileRemoved = false;
    609                     for (int i = 0; i < childCount; i++) {
    610                         CheckableFrameLayout c =
    611                                 (CheckableFrameLayout) mWallpapersView.getChildAt(i);
    612                         if (c.isChecked()) {
    613                             WallpaperTileInfo info = (WallpaperTileInfo) c.getTag();
    614                             info.onDelete(WallpaperPickerActivity.this);
    615                             viewsToRemove.add(c);
    616                             if (i == mSelectedIndex) {
    617                                 selectedTileRemoved = true;
    618                             }
    619                         }
    620                     }
    621                     for (View v : viewsToRemove) {
    622                         mWallpapersView.removeView(v);
    623                     }
    624                     if (selectedTileRemoved) {
    625                         mSelectedIndex = -1;
    626                         mSelectedTile = null;
    627                         setSystemWallpaperVisiblity(true);
    628                     }
    629                     updateTileIndices();
    630                     mode.finish(); // Action picked, so close the CAB
    631                     return true;
    632                 } else {
    633                     return false;
    634                 }
    635             }
    636 
    637             // Called when the user exits the action mode
    638             @Override
    639             public void onDestroyActionMode(ActionMode mode) {
    640                 int childCount = mWallpapersView.getChildCount();
    641                 for (int i = 0; i < childCount; i++) {
    642                     CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
    643                     c.setChecked(false);
    644                 }
    645                 if (mSelectedTile != null) {
    646                     mSelectedTile.setSelected(true);
    647                 }
    648                 mActionMode = null;
    649             }
    650         };
    651     }
    652 
    653     public void setWallpaperButtonEnabled(boolean enabled) {
    654         mSetWallpaperButton.setEnabled(enabled);
    655     }
    656 
    657     @Thunk void selectTile(View v) {
    658         if (mSelectedTile != null) {
    659             mSelectedTile.setSelected(false);
    660             mSelectedTile = null;
    661         }
    662         mSelectedTile = v;
    663         v.setSelected(true);
    664         mSelectedIndex = mWallpapersView.indexOfChild(v);
    665         // TODO: Remove this once the accessibility framework and
    666         // services have better support for selection state.
    667         v.announceForAccessibility(
    668                 getContext().getString(R.string.announce_selection, v.getContentDescription()));
    669     }
    670 
    671     @Thunk void initializeScrollForRtl() {
    672         if (Utilities.isRtl(getResources())) {
    673             final ViewTreeObserver observer = mWallpaperScrollContainer.getViewTreeObserver();
    674             observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    675                 public void onGlobalLayout() {
    676                     LinearLayout masterWallpaperList =
    677                             (LinearLayout) findViewById(R.id.master_wallpaper_list);
    678                     mWallpaperScrollContainer.scrollTo(masterWallpaperList.getWidth(), 0);
    679                     mWallpaperScrollContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    680                 }
    681             });
    682         }
    683     }
    684 
    685     protected Bitmap getThumbnailOfLastPhoto() {
    686         boolean canReadExternalStorage = getActivity().checkPermission(
    687                 Manifest.permission.READ_EXTERNAL_STORAGE, Process.myPid(), Process.myUid()) ==
    688                 PackageManager.PERMISSION_GRANTED;
    689 
    690         if (!canReadExternalStorage) {
    691             // MediaStore.Images.Media.EXTERNAL_CONTENT_URI requires
    692             // the READ_EXTERNAL_STORAGE permission
    693             return null;
    694         }
    695 
    696         Cursor cursor = MediaStore.Images.Media.query(getContext().getContentResolver(),
    697                 MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
    698                 new String[] { MediaStore.Images.ImageColumns._ID,
    699                     MediaStore.Images.ImageColumns.DATE_TAKEN},
    700                 null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC LIMIT 1");
    701 
    702         Bitmap thumb = null;
    703         if (cursor != null) {
    704             if (cursor.moveToNext()) {
    705                 int id = cursor.getInt(0);
    706                 thumb = MediaStore.Images.Thumbnails.getThumbnail(getContext().getContentResolver(),
    707                         id, MediaStore.Images.Thumbnails.MINI_KIND, null);
    708             }
    709             cursor.close();
    710         }
    711         return thumb;
    712     }
    713 
    714     public void onStop() {
    715         super.onStop();
    716         mWallpaperScrollContainer = (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container);
    717         if (mWallpaperScrollContainer.getAlpha() < 1f) {
    718             mWallpaperScrollContainer.setAlpha(1f);
    719             mWallpaperScrollContainer.setVisibility(View.VISIBLE);
    720         }
    721     }
    722 
    723     public void onSaveInstanceState(Bundle outState) {
    724         outState.putParcelableArrayList(TEMP_WALLPAPER_TILES, mTempWallpaperTiles);
    725         outState.putInt(SELECTED_INDEX, mSelectedIndex);
    726     }
    727 
    728     protected void onRestoreInstanceState(Bundle savedInstanceState) {
    729         ArrayList<Uri> uris = savedInstanceState.getParcelableArrayList(TEMP_WALLPAPER_TILES);
    730         for (Uri uri : uris) {
    731             addTemporaryWallpaperTile(uri, true);
    732         }
    733         mSelectedIndex = savedInstanceState.getInt(SELECTED_INDEX, -1);
    734     }
    735 
    736     @Thunk void populateWallpapersFromAdapter(ViewGroup parent, BaseAdapter adapter,
    737             boolean addLongPressHandler) {
    738         for (int i = 0; i < adapter.getCount(); i++) {
    739             FrameLayout thumbnail = (FrameLayout) adapter.getView(i, null, parent);
    740             parent.addView(thumbnail, i);
    741             WallpaperTileInfo info = (WallpaperTileInfo) adapter.getItem(i);
    742             thumbnail.setTag(info);
    743             info.setView(thumbnail);
    744             if (addLongPressHandler) {
    745                 addLongPressHandler(thumbnail);
    746             }
    747             thumbnail.setOnClickListener(mThumbnailOnClickListener);
    748         }
    749     }
    750 
    751     @Thunk void updateTileIndices() {
    752         LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list);
    753         final int childCount = masterWallpaperList.getChildCount();
    754         final Resources res = getResources();
    755 
    756         // Do two passes; the first pass gets the total number of tiles
    757         int numTiles = 0;
    758         for (int passNum = 0; passNum < 2; passNum++) {
    759             int tileIndex = 0;
    760             for (int i = 0; i < childCount; i++) {
    761                 View child = masterWallpaperList.getChildAt(i);
    762                 LinearLayout subList;
    763 
    764                 int subListStart;
    765                 int subListEnd;
    766                 if (child.getTag() instanceof WallpaperTileInfo) {
    767                     subList = masterWallpaperList;
    768                     subListStart = i;
    769                     subListEnd = i + 1;
    770                 } else { // if (child instanceof LinearLayout) {
    771                     subList = (LinearLayout) child;
    772                     subListStart = 0;
    773                     subListEnd = subList.getChildCount();
    774                 }
    775 
    776                 for (int j = subListStart; j < subListEnd; j++) {
    777                     WallpaperTileInfo info = (WallpaperTileInfo) subList.getChildAt(j).getTag();
    778                     if (info.isNamelessWallpaper()) {
    779                         if (passNum == 0) {
    780                             numTiles++;
    781                         } else {
    782                             CharSequence label = res.getString(
    783                                     R.string.wallpaper_accessibility_name, ++tileIndex, numTiles);
    784                             info.onIndexUpdated(label);
    785                         }
    786                     }
    787                 }
    788             }
    789         }
    790     }
    791 
    792     @Thunk static Point getDefaultThumbnailSize(Resources res) {
    793         return new Point(res.getDimensionPixelSize(R.dimen.wallpaperThumbnailWidth),
    794                 res.getDimensionPixelSize(R.dimen.wallpaperThumbnailHeight));
    795 
    796     }
    797 
    798     @Thunk static Bitmap createThumbnail(Point size, Context context, Uri uri, byte[] imageBytes,
    799             Resources res, int resId, int rotation, boolean leftAligned) {
    800         int width = size.x;
    801         int height = size.y;
    802 
    803         BitmapCropTask cropTask;
    804         if (uri != null) {
    805             cropTask = new BitmapCropTask(
    806                     context, uri, null, rotation, width, height, false, true, null);
    807         } else if (imageBytes != null) {
    808             cropTask = new BitmapCropTask(
    809                     imageBytes, null, rotation, width, height, false, true, null);
    810         }  else {
    811             cropTask = new BitmapCropTask(
    812                     context, res, resId, null, rotation, width, height, false, true, null);
    813         }
    814         Point bounds = cropTask.getImageBounds();
    815         if (bounds == null || bounds.x == 0 || bounds.y == 0) {
    816             return null;
    817         }
    818 
    819         Matrix rotateMatrix = new Matrix();
    820         rotateMatrix.setRotate(rotation);
    821         float[] rotatedBounds = new float[] { bounds.x, bounds.y };
    822         rotateMatrix.mapPoints(rotatedBounds);
    823         rotatedBounds[0] = Math.abs(rotatedBounds[0]);
    824         rotatedBounds[1] = Math.abs(rotatedBounds[1]);
    825 
    826         RectF cropRect = Utils.getMaxCropRect(
    827                 (int) rotatedBounds[0], (int) rotatedBounds[1], width, height, leftAligned);
    828         cropTask.setCropBounds(cropRect);
    829 
    830         if (cropTask.cropBitmap()) {
    831             return cropTask.getCroppedBitmap();
    832         } else {
    833             return null;
    834         }
    835     }
    836 
    837     private void addTemporaryWallpaperTile(final Uri uri, boolean fromRestore) {
    838         mTempWallpaperTiles.add(uri);
    839         // Add a tile for the image picked from Gallery
    840         final FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
    841                 inflate(R.layout.wallpaper_picker_item, mWallpapersView, false);
    842         pickedImageThumbnail.setVisibility(View.GONE);
    843         mWallpapersView.addView(pickedImageThumbnail, 0);
    844 
    845         // Load the thumbnail
    846         final ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
    847         final Point defaultSize = getDefaultThumbnailSize(this.getResources());
    848         final Context context = getContext();
    849         new AsyncTask<Void, Bitmap, Bitmap>() {
    850             protected Bitmap doInBackground(Void...args) {
    851                 try {
    852                     int rotation = BitmapUtils.getRotationFromExif(context, uri);
    853                     return createThumbnail(defaultSize, context, uri, null, null, 0, rotation, false);
    854                 } catch (SecurityException securityException) {
    855                     if (isActivityDestroyed()) {
    856                         // Temporarily granted permissions are revoked when the activity
    857                         // finishes, potentially resulting in a SecurityException here.
    858                         // Even though {@link #isDestroyed} might also return true in different
    859                         // situations where the configuration changes, we are fine with
    860                         // catching these cases here as well.
    861                         cancel(false);
    862                     } else {
    863                         // otherwise it had a different cause and we throw it further
    864                         throw securityException;
    865                     }
    866                     return null;
    867                 }
    868             }
    869             protected void onPostExecute(Bitmap thumb) {
    870                 if (!isCancelled() && thumb != null) {
    871                     image.setImageBitmap(thumb);
    872                     Drawable thumbDrawable = image.getDrawable();
    873                     thumbDrawable.setDither(true);
    874                     pickedImageThumbnail.setVisibility(View.VISIBLE);
    875                 } else {
    876                     Log.e(TAG, "Error loading thumbnail for uri=" + uri);
    877                 }
    878             }
    879         }.execute();
    880 
    881         UriWallpaperInfo info = new UriWallpaperInfo(uri);
    882         pickedImageThumbnail.setTag(info);
    883         info.setView(pickedImageThumbnail);
    884         addLongPressHandler(pickedImageThumbnail);
    885         updateTileIndices();
    886         pickedImageThumbnail.setOnClickListener(mThumbnailOnClickListener);
    887         if (!fromRestore) {
    888             mThumbnailOnClickListener.onClick(pickedImageThumbnail);
    889         }
    890     }
    891 
    892     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    893         if (requestCode == IMAGE_PICK && resultCode == Activity.RESULT_OK) {
    894             if (data != null && data.getData() != null) {
    895                 Uri uri = data.getData();
    896                 addTemporaryWallpaperTile(uri, false);
    897             }
    898         } else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY
    899                 && resultCode == Activity.RESULT_OK) {
    900             // Something was set on the third-party activity.
    901             setResult(Activity.RESULT_OK);
    902             finish();
    903         }
    904     }
    905 
    906     private void addLongPressHandler(View v) {
    907         v.setOnLongClickListener(mLongClickListener);
    908 
    909         // Enable stylus button to also trigger long click.
    910         final StylusEventHelper stylusEventHelper = new StylusEventHelper(v);
    911         v.setOnTouchListener(new View.OnTouchListener() {
    912             @Override
    913             public boolean onTouch(View view, MotionEvent event) {
    914                 return stylusEventHelper.checkAndPerformStylusEvent(event);
    915             }
    916         });
    917     }
    918 
    919     private ArrayList<WallpaperTileInfo> findBundledWallpapers() {
    920         final PackageManager pm = getContext().getPackageManager();
    921         final ArrayList<WallpaperTileInfo> bundled = new ArrayList<WallpaperTileInfo>(24);
    922 
    923         Partner partner = Partner.get(pm);
    924         if (partner != null) {
    925             final Resources partnerRes = partner.getResources();
    926             final int resId = partnerRes.getIdentifier(Partner.RES_WALLPAPERS, "array",
    927                     partner.getPackageName());
    928             if (resId != 0) {
    929                 addWallpapers(bundled, partnerRes, partner.getPackageName(), resId);
    930             }
    931 
    932             // Add system wallpapers
    933             File systemDir = partner.getWallpaperDirectory();
    934             if (systemDir != null && systemDir.isDirectory()) {
    935                 for (File file : systemDir.listFiles()) {
    936                     if (!file.isFile()) {
    937                         continue;
    938                     }
    939                     String name = file.getName();
    940                     int dotPos = name.lastIndexOf('.');
    941                     String extension = "";
    942                     if (dotPos >= -1) {
    943                         extension = name.substring(dotPos);
    944                         name = name.substring(0, dotPos);
    945                     }
    946 
    947                     if (name.endsWith("_small")) {
    948                         // it is a thumbnail
    949                         continue;
    950                     }
    951 
    952                     File thumbnail = new File(systemDir, name + "_small" + extension);
    953                     Bitmap thumb = BitmapFactory.decodeFile(thumbnail.getAbsolutePath());
    954                     if (thumb != null) {
    955                         bundled.add(new FileWallpaperInfo(file, new BitmapDrawable(thumb)));
    956                     }
    957                 }
    958             }
    959         }
    960 
    961         Pair<ApplicationInfo, Integer> r = getWallpaperArrayResourceId();
    962         if (r != null) {
    963             try {
    964                 Resources wallpaperRes = getContext().getPackageManager()
    965                         .getResourcesForApplication(r.first);
    966                 addWallpapers(bundled, wallpaperRes, r.first.packageName, r.second);
    967             } catch (PackageManager.NameNotFoundException e) {
    968             }
    969         }
    970 
    971         if (partner == null || !partner.hideDefaultWallpaper()) {
    972             // Add an entry for the default wallpaper (stored in system resources)
    973             WallpaperTileInfo defaultWallpaperInfo =
    974                     (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
    975                     ? getPreKKDefaultWallpaperInfo()
    976                     : getDefaultWallpaper();
    977             if (defaultWallpaperInfo != null) {
    978                 bundled.add(0, defaultWallpaperInfo);
    979             }
    980         }
    981         return bundled;
    982     }
    983 
    984     private boolean writeImageToFileAsJpeg(File f, Bitmap b) {
    985         try {
    986             f.createNewFile();
    987             FileOutputStream thumbFileStream =
    988                     getContext().openFileOutput(f.getName(), Context.MODE_PRIVATE);
    989             b.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream);
    990             thumbFileStream.close();
    991             return true;
    992         } catch (IOException e) {
    993             Log.e(TAG, "Error while writing bitmap to file " + e);
    994             f.delete();
    995         }
    996         return false;
    997     }
    998 
    999     private File getDefaultThumbFile() {
   1000         return new File(getContext().getFilesDir(), Build.VERSION.SDK_INT
   1001                 + "_" + LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL);
   1002     }
   1003 
   1004     private boolean saveDefaultWallpaperThumb(Bitmap b) {
   1005         // Delete old thumbnails.
   1006         new File(getContext().getFilesDir(), LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL_OLD).delete();
   1007         new File(getContext().getFilesDir(), LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL).delete();
   1008 
   1009         for (int i = Build.VERSION_CODES.JELLY_BEAN; i < Build.VERSION.SDK_INT; i++) {
   1010             new File(getContext().getFilesDir(), i + "_"
   1011                     + LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL).delete();
   1012         }
   1013         return writeImageToFileAsJpeg(getDefaultThumbFile(), b);
   1014     }
   1015 
   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 = BitmapUtils.getRotationFromExif(res, resId);
   1030             thumb = createThumbnail(
   1031                     defaultThumbSize, getContext(), 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)
   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(getContext()).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 
   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 = getContext().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 
   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 
   1107     public CropView getCropView() {
   1108         return mCropView;
   1109     }
   1110 
   1111     public SavedWallpaperImages getSavedImages() {
   1112         return mSavedImages;
   1113     }
   1114 
   1115     private static class SimpleWallpapersAdapter extends ArrayAdapter<WallpaperTileInfo> {
   1116         private final LayoutInflater mLayoutInflater;
   1117 
   1118         SimpleWallpapersAdapter(Context context, ArrayList<WallpaperTileInfo> wallpapers) {
   1119             super(context, R.layout.wallpaper_picker_item, wallpapers);
   1120             mLayoutInflater = LayoutInflater.from(context);
   1121         }
   1122 
   1123         public View getView(int position, View convertView, ViewGroup parent) {
   1124             Drawable thumb = getItem(position).mThumb;
   1125             if (thumb == null) {
   1126                 Log.e(TAG, "Error decoding thumbnail for wallpaper #" + position);
   1127             }
   1128             return createImageTileView(mLayoutInflater, convertView, parent, thumb);
   1129         }
   1130     }
   1131 
   1132     public static View createImageTileView(LayoutInflater layoutInflater,
   1133             View convertView, ViewGroup parent, Drawable thumb) {
   1134         View view;
   1135 
   1136         if (convertView == null) {
   1137             view = layoutInflater.inflate(R.layout.wallpaper_picker_item, parent, false);
   1138         } else {
   1139             view = convertView;
   1140         }
   1141 
   1142         ImageView image = (ImageView) view.findViewById(R.id.wallpaper_image);
   1143 
   1144         if (thumb != null) {
   1145             image.setImageDrawable(thumb);
   1146             thumb.setDither(true);
   1147         }
   1148 
   1149         return view;
   1150     }
   1151 
   1152     public void startActivityForResultSafely(Intent intent, int requestCode) {
   1153         Utilities.startActivityForResultSafely(getActivity(), intent, requestCode);
   1154     }
   1155 
   1156     @Override
   1157     public boolean enableRotation() {
   1158         // Check if rotation is enabled for this device.
   1159         if (Utilities.isRotationAllowedForDevice(getContext()))
   1160             return true;
   1161 
   1162         // Check if the user has specifically enabled rotation via preferences.
   1163         return Utilities.isAllowRotationPrefEnabled(getApplicationContext(), true);
   1164     }
   1165 }
   1166