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