• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
setView(View v)113         public void setView(View v) {
114             mView = v;
115         }
onClick(WallpaperPickerActivity a)116         public void onClick(WallpaperPickerActivity a) {}
onSave(WallpaperPickerActivity a)117         public void onSave(WallpaperPickerActivity a) {}
onDelete(WallpaperPickerActivity a)118         public void onDelete(WallpaperPickerActivity a) {}
isSelectable()119         public boolean isSelectable() { return false; }
isNamelessWallpaper()120         public boolean isNamelessWallpaper() { return false; }
onIndexUpdated(CharSequence label)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
onClick(WallpaperPickerActivity a)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;
UriWallpaperInfo(Uri uri)141         public UriWallpaperInfo(Uri uri) {
142             mUri = uri;
143         }
144         @Override
onClick(final WallpaperPickerActivity a)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
onSave(final WallpaperPickerActivity a)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
isSelectable()189         public boolean isSelectable() {
190             return true;
191         }
192         @Override
isNamelessWallpaper()193         public boolean isNamelessWallpaper() {
194             return true;
195         }
196     }
197 
198     public static class FileWallpaperInfo extends WallpaperTileInfo {
199         private File mFile;
200 
FileWallpaperInfo(File target, Drawable thumb)201         public FileWallpaperInfo(File target, Drawable thumb) {
202             mFile = target;
203             mThumb = thumb;
204         }
205         @Override
onClick(WallpaperPickerActivity a)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
onSave(WallpaperPickerActivity a)212         public void onSave(WallpaperPickerActivity a) {
213             a.setWallpaper(Uri.fromFile(mFile), true);
214         }
215         @Override
isSelectable()216         public boolean isSelectable() {
217             return true;
218         }
219         @Override
isNamelessWallpaper()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 
ResourceWallpaperInfo(Resources res, int resId, Drawable thumb)229         public ResourceWallpaperInfo(Resources res, int resId, Drawable thumb) {
230             mResources = res;
231             mResId = resId;
232             mThumb = thumb;
233         }
234         @Override
onClick(WallpaperPickerActivity a)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
onSave(WallpaperPickerActivity a)253         public void onSave(WallpaperPickerActivity a) {
254             boolean finishActivityWhenDone = true;
255             a.cropImageAndSetWallpaper(mResources, mResId, finishActivityWhenDone);
256         }
257         @Override
isSelectable()258         public boolean isSelectable() {
259             return true;
260         }
261         @Override
isNamelessWallpaper()262         public boolean isNamelessWallpaper() {
263             return true;
264         }
265     }
266 
267     @TargetApi(Build.VERSION_CODES.KITKAT)
268     public static class DefaultWallpaperInfo extends WallpaperTileInfo {
DefaultWallpaperInfo(Drawable thumb)269         public DefaultWallpaperInfo(Drawable thumb) {
270             mThumb = thumb;
271         }
272         @Override
onClick(WallpaperPickerActivity a)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
onSave(WallpaperPickerActivity a)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
isSelectable()302         public boolean isSelectable() {
303             return true;
304         }
305         @Override
isNamelessWallpaper()306         public boolean isNamelessWallpaper() {
307             return true;
308         }
309     }
310 
setWallpaperStripYOffset(float offset)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      */
setSystemWallpaperVisiblity(final boolean visible)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 
changeWallpaperFlags(boolean visible)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
setCropViewTileSource(BitmapSource bitmapSource, boolean touchEnabled, boolean moveToLeft, final Runnable postExecute)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
init()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 
selectTile(View v)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 
initializeScrollForRtl()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 
getThumbnailOfLastPhoto()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 
onStop()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 
onSaveInstanceState(Bundle outState)712     protected void onSaveInstanceState(Bundle outState) {
713         outState.putParcelableArrayList(TEMP_WALLPAPER_TILES, mTempWallpaperTiles);
714         outState.putInt(SELECTED_INDEX, mSelectedIndex);
715     }
716 
onRestoreInstanceState(Bundle savedInstanceState)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 
populateWallpapersFromAdapter(ViewGroup parent, BaseAdapter adapter, boolean addLongPressHandler)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 
updateTileIndices()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 
getDefaultThumbnailSize(Resources res)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 
createThumbnail(Point size, Context context, Uri uri, byte[] imageBytes, Resources res, int resId, int rotation, boolean leftAligned)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 
addTemporaryWallpaperTile(final Uri uri, boolean fromRestore)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 
onActivityResult(int requestCode, int resultCode, Intent data)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 
setWallpaperItemPaddingToZero(FrameLayout frameLayout)910     static void setWallpaperItemPaddingToZero(FrameLayout frameLayout) {
911         frameLayout.setPadding(0, 0, 0, 0);
912         frameLayout.setForeground(new ZeroPaddingDrawable(frameLayout.getForeground()));
913     }
914 
addLongPressHandler(View v)915     private void addLongPressHandler(View v) {
916         v.setOnLongClickListener(mLongClickListener);
917     }
918 
findBundledWallpapers()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 
writeImageToFileAsJpeg(File f, Bitmap b)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 
getDefaultThumbFile()998     private File getDefaultThumbFile() {
999         return new File(getFilesDir(), Build.VERSION.SDK_INT
1000                 + "_" + LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL);
1001     }
1002 
saveDefaultWallpaperThumb(Bitmap b)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 
getPreKKDefaultWallpaperInfo()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)
getDefaultWallpaper()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 
getWallpaperArrayResourceId()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 
addWallpapers(ArrayList<WallpaperTileInfo> known, Resources res, String packageName, int listResId)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 
getCropView()1105     public CropView getCropView() {
1106         return mCropView;
1107     }
1108 
getSavedImages()1109     public SavedWallpaperImages getSavedImages() {
1110         return mSavedImages;
1111     }
1112 
onLiveWallpaperPickerLaunch(WallpaperInfo info)1113     public void onLiveWallpaperPickerLaunch(WallpaperInfo info) {
1114         mLastClickedLiveWallpaperInfo = info;
1115         mLiveWallpaperInfoOnPickerLaunch = WallpaperManager.getInstance(this).getWallpaperInfo();
1116     }
1117 
1118     static class ZeroPaddingDrawable extends LevelListDrawable {
ZeroPaddingDrawable(Drawable d)1119         public ZeroPaddingDrawable(Drawable d) {
1120             super();
1121             addLevel(0, 0, d);
1122             setLevel(0);
1123         }
1124 
1125         @Override
getPadding(Rect padding)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 
SimpleWallpapersAdapter(Activity activity, ArrayList<WallpaperTileInfo> wallpapers)1135         SimpleWallpapersAdapter(Activity activity, ArrayList<WallpaperTileInfo> wallpapers) {
1136             super(activity, R.layout.wallpaper_picker_item, wallpapers);
1137             mLayoutInflater = activity.getLayoutInflater();
1138         }
1139 
getView(int position, View convertView, ViewGroup parent)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 
createImageTileView(LayoutInflater layoutInflater, View convertView, ViewGroup parent, Drawable thumb)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
startActivityForResultSafely(Intent intent, int requestCode)1173     public void startActivityForResultSafely(Intent intent, int requestCode) {
1174         startActivityForResult(intent, requestCode);
1175     }
1176 }
1177