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