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