Home | History | Annotate | Download | only in launcher3
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.launcher3;
     18 
     19 import android.Manifest;
     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 
    125         public void setView(View v) {
    126             mView = v;
    127         }
    128         public void onClick(WallpaperPickerActivity a) {}
    129         public void onSave(WallpaperPickerActivity a) {}
    130         public void onDelete(WallpaperPickerActivity a) {}
    131         public boolean isSelectable() { return false; }
    132         public boolean isNamelessWallpaper() { return false; }
    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
    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;
    151         public UriWallpaperInfo(Uri uri) {
    152             mUri = uri;
    153         }
    154         @Override
    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
    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
    226         public boolean isSelectable() {
    227             return true;
    228         }
    229         @Override
    230         public boolean isNamelessWallpaper() {
    231             return true;
    232         }
    233     }
    234 
    235     public static class FileWallpaperInfo extends WallpaperTileInfo {
    236         protected File mFile;
    237 
    238         public FileWallpaperInfo(File target, Drawable thumb) {
    239             mFile = target;
    240             mThumb = thumb;
    241         }
    242         @Override
    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 
    259         protected CropViewScaleAndOffsetProvider getCropViewScaleAndOffsetProvider() {
    260             return null;
    261         }
    262 
    263         @Override
    264         public void onSave(WallpaperPickerActivity a) {
    265             boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f;
    266             a.setWallpaper(Uri.fromFile(mFile), true, shouldFadeOutOnFinish);
    267         }
    268         @Override
    269         public boolean isSelectable() {
    270             return true;
    271         }
    272         @Override
    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 
    282         public ResourceWallpaperInfo(Resources res, int resId, Drawable thumb) {
    283             mResources = res;
    284             mResId = resId;
    285             mThumb = thumb;
    286         }
    287         @Override
    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
    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
    321         public boolean isSelectable() {
    322             return true;
    323         }
    324         @Override
    325         public boolean isNamelessWallpaper() {
    326             return true;
    327         }
    328     }
    329 
    330     @TargetApi(Build.VERSION_CODES.KITKAT)
    331     public static class DefaultWallpaperInfo extends WallpaperTileInfo {
    332         public DefaultWallpaperInfo(Drawable thumb) {
    333             mThumb = thumb;
    334         }
    335         @Override
    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
    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
    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
    425         public boolean isSelectable() {
    426             return true;
    427         }
    428         @Override
    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