Home | History | Annotate | Download | only in launcher3
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.launcher3;
     18 
     19 import android.animation.Animator;
     20 import android.animation.LayoutTransition;
     21 import android.app.ActionBar;
     22 import android.app.Activity;
     23 import android.app.WallpaperInfo;
     24 import android.app.WallpaperManager;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.pm.ApplicationInfo;
     28 import android.content.pm.PackageManager;
     29 import android.content.res.Resources;
     30 import android.database.Cursor;
     31 import android.database.DataSetObserver;
     32 import android.graphics.Bitmap;
     33 import android.graphics.BitmapFactory;
     34 import android.graphics.Matrix;
     35 import android.graphics.Point;
     36 import android.graphics.PorterDuff;
     37 import android.graphics.Rect;
     38 import android.graphics.RectF;
     39 import android.graphics.drawable.BitmapDrawable;
     40 import android.graphics.drawable.Drawable;
     41 import android.graphics.drawable.LevelListDrawable;
     42 import android.net.Uri;
     43 import android.os.Bundle;
     44 import android.provider.MediaStore;
     45 import android.util.Log;
     46 import android.util.Pair;
     47 import android.view.ActionMode;
     48 import android.view.LayoutInflater;
     49 import android.view.Menu;
     50 import android.view.MenuInflater;
     51 import android.view.MenuItem;
     52 import android.view.View;
     53 import android.view.View.OnClickListener;
     54 import android.view.ViewGroup;
     55 import android.view.ViewTreeObserver;
     56 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
     57 import android.view.accessibility.AccessibilityEvent;
     58 import android.view.animation.AccelerateInterpolator;
     59 import android.view.animation.DecelerateInterpolator;
     60 import android.widget.BaseAdapter;
     61 import android.widget.FrameLayout;
     62 import android.widget.HorizontalScrollView;
     63 import android.widget.ImageView;
     64 import android.widget.LinearLayout;
     65 import android.widget.ListAdapter;
     66 
     67 import com.android.gallery3d.exif.ExifInterface;
     68 import com.android.photos.BitmapRegionTileSource;
     69 
     70 import java.io.BufferedInputStream;
     71 import java.io.File;
     72 import java.io.FileOutputStream;
     73 import java.io.IOException;
     74 import java.io.InputStream;
     75 import java.util.ArrayList;
     76 
     77 public class WallpaperPickerActivity extends WallpaperCropActivity {
     78     static final String TAG = "Launcher.WallpaperPickerActivity";
     79 
     80     public static final int IMAGE_PICK = 5;
     81     public static final int PICK_WALLPAPER_THIRD_PARTY_ACTIVITY = 6;
     82     public static final int PICK_LIVE_WALLPAPER = 7;
     83     private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES";
     84 
     85     private View mSelectedThumb;
     86     private boolean mIgnoreNextTap;
     87     private OnClickListener mThumbnailOnClickListener;
     88 
     89     private LinearLayout mWallpapersView;
     90     private View mWallpaperStrip;
     91 
     92     private ActionMode.Callback mActionModeCallback;
     93     private ActionMode mActionMode;
     94 
     95     private View.OnLongClickListener mLongClickListener;
     96 
     97     ArrayList<Uri> mTempWallpaperTiles = new ArrayList<Uri>();
     98     private SavedWallpaperImages mSavedImages;
     99     private WallpaperInfo mLiveWallpaperInfoOnPickerLaunch;
    100 
    101     public static abstract class WallpaperTileInfo {
    102         protected View mView;
    103         public void setView(View v) {
    104             mView = v;
    105         }
    106         public void onClick(WallpaperPickerActivity a) {}
    107         public void onSave(WallpaperPickerActivity a) {}
    108         public void onDelete(WallpaperPickerActivity a) {}
    109         public boolean isSelectable() { return false; }
    110         public boolean isNamelessWallpaper() { return false; }
    111         public void onIndexUpdated(CharSequence label) {
    112             if (isNamelessWallpaper()) {
    113                 mView.setContentDescription(label);
    114             }
    115         }
    116     }
    117 
    118     public static class PickImageInfo extends WallpaperTileInfo {
    119         @Override
    120         public void onClick(WallpaperPickerActivity a) {
    121             Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    122             intent.setType("image/*");
    123             Utilities.startActivityForResultSafely(a, intent, IMAGE_PICK);
    124         }
    125     }
    126 
    127     public static class UriWallpaperInfo extends WallpaperTileInfo {
    128         private Uri mUri;
    129         public UriWallpaperInfo(Uri uri) {
    130             mUri = uri;
    131         }
    132         @Override
    133         public void onClick(WallpaperPickerActivity a) {
    134             CropView v = a.getCropView();
    135             int rotation = WallpaperCropActivity.getRotationFromExif(a, mUri);
    136             v.setTileSource(new BitmapRegionTileSource(a, mUri, 1024, rotation), null);
    137             v.setTouchEnabled(true);
    138         }
    139         @Override
    140         public void onSave(final WallpaperPickerActivity a) {
    141             boolean finishActivityWhenDone = true;
    142             OnBitmapCroppedHandler h = new OnBitmapCroppedHandler() {
    143                 public void onBitmapCropped(byte[] imageBytes) {
    144                     Point thumbSize = getDefaultThumbnailSize(a.getResources());
    145                     // rotation is set to 0 since imageBytes has already been correctly rotated
    146                     Bitmap thumb = createThumbnail(
    147                             thumbSize, null, null, imageBytes, null, 0, 0, true);
    148                     a.getSavedImages().writeImage(thumb, imageBytes);
    149                 }
    150             };
    151             a.cropImageAndSetWallpaper(mUri, h, finishActivityWhenDone);
    152         }
    153         @Override
    154         public boolean isSelectable() {
    155             return true;
    156         }
    157         @Override
    158         public boolean isNamelessWallpaper() {
    159             return true;
    160         }
    161     }
    162 
    163     public static class ResourceWallpaperInfo extends WallpaperTileInfo {
    164         private Resources mResources;
    165         private int mResId;
    166         private Drawable mThumb;
    167 
    168         public ResourceWallpaperInfo(Resources res, int resId, Drawable thumb) {
    169             mResources = res;
    170             mResId = resId;
    171             mThumb = thumb;
    172         }
    173         @Override
    174         public void onClick(WallpaperPickerActivity a) {
    175             int rotation = WallpaperCropActivity.getRotationFromExif(mResources, mResId);
    176             BitmapRegionTileSource source = new BitmapRegionTileSource(
    177                     mResources, a, mResId, 1024, rotation);
    178             CropView v = a.getCropView();
    179             v.setTileSource(source, null);
    180             Point wallpaperSize = WallpaperCropActivity.getDefaultWallpaperSize(
    181                     a.getResources(), a.getWindowManager());
    182             RectF crop = WallpaperCropActivity.getMaxCropRect(
    183                     source.getImageWidth(), source.getImageHeight(),
    184                     wallpaperSize.x, wallpaperSize.y, false);
    185             v.setScale(wallpaperSize.x / crop.width());
    186             v.setTouchEnabled(false);
    187         }
    188         @Override
    189         public void onSave(WallpaperPickerActivity a) {
    190             boolean finishActivityWhenDone = true;
    191             a.cropImageAndSetWallpaper(mResources, mResId, finishActivityWhenDone);
    192         }
    193         @Override
    194         public boolean isSelectable() {
    195             return true;
    196         }
    197         @Override
    198         public boolean isNamelessWallpaper() {
    199             return true;
    200         }
    201     }
    202 
    203     public void setWallpaperStripYOffset(float offset) {
    204         mWallpaperStrip.setPadding(0, 0, 0, (int) offset);
    205     }
    206 
    207     // called by onCreate; this is subclassed to overwrite WallpaperCropActivity
    208     protected void init() {
    209         setContentView(R.layout.wallpaper_picker);
    210 
    211         mCropView = (CropView) findViewById(R.id.cropView);
    212         mWallpaperStrip = findViewById(R.id.wallpaper_strip);
    213         mCropView.setTouchCallback(new CropView.TouchCallback() {
    214             LauncherViewPropertyAnimator mAnim;
    215             @Override
    216             public void onTouchDown() {
    217                 if (mAnim != null) {
    218                     mAnim.cancel();
    219                 }
    220                 if (mWallpaperStrip.getAlpha() == 1f) {
    221                     mIgnoreNextTap = true;
    222                 }
    223                 mAnim = new LauncherViewPropertyAnimator(mWallpaperStrip);
    224                 mAnim.alpha(0f)
    225                      .setDuration(150)
    226                      .addListener(new Animator.AnimatorListener() {
    227                          public void onAnimationStart(Animator animator) { }
    228                          public void onAnimationEnd(Animator animator) {
    229                              mWallpaperStrip.setVisibility(View.INVISIBLE);
    230                          }
    231                          public void onAnimationCancel(Animator animator) { }
    232                          public void onAnimationRepeat(Animator animator) { }
    233                      });
    234                 mAnim.setInterpolator(new AccelerateInterpolator(0.75f));
    235                 mAnim.start();
    236             }
    237             @Override
    238             public void onTouchUp() {
    239                 mIgnoreNextTap = false;
    240             }
    241             @Override
    242             public void onTap() {
    243                 boolean ignoreTap = mIgnoreNextTap;
    244                 mIgnoreNextTap = false;
    245                 if (!ignoreTap) {
    246                     if (mAnim != null) {
    247                         mAnim.cancel();
    248                     }
    249                     mWallpaperStrip.setVisibility(View.VISIBLE);
    250                     mAnim = new LauncherViewPropertyAnimator(mWallpaperStrip);
    251                     mAnim.alpha(1f)
    252                          .setDuration(150)
    253                          .setInterpolator(new DecelerateInterpolator(0.75f));
    254                     mAnim.start();
    255                 }
    256             }
    257         });
    258 
    259         mThumbnailOnClickListener = new OnClickListener() {
    260             public void onClick(View v) {
    261                 if (mActionMode != null) {
    262                     // When CAB is up, clicking toggles the item instead
    263                     if (v.isLongClickable()) {
    264                         mLongClickListener.onLongClick(v);
    265                     }
    266                     return;
    267                 }
    268                 WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
    269                 if (info.isSelectable()) {
    270                     if (mSelectedThumb != null) {
    271                         mSelectedThumb.setSelected(false);
    272                         mSelectedThumb = null;
    273                     }
    274                     mSelectedThumb = v;
    275                     v.setSelected(true);
    276                     // TODO: Remove this once the accessibility framework and
    277                     // services have better support for selection state.
    278                     v.announceForAccessibility(
    279                             getString(R.string.announce_selection, v.getContentDescription()));
    280                 }
    281                 info.onClick(WallpaperPickerActivity.this);
    282             }
    283         };
    284         mLongClickListener = new View.OnLongClickListener() {
    285             // Called when the user long-clicks on someView
    286             public boolean onLongClick(View view) {
    287                 CheckableFrameLayout c = (CheckableFrameLayout) view;
    288                 c.toggle();
    289 
    290                 if (mActionMode != null) {
    291                     mActionMode.invalidate();
    292                 } else {
    293                     // Start the CAB using the ActionMode.Callback defined below
    294                     mActionMode = startActionMode(mActionModeCallback);
    295                     int childCount = mWallpapersView.getChildCount();
    296                     for (int i = 0; i < childCount; i++) {
    297                         mWallpapersView.getChildAt(i).setSelected(false);
    298                     }
    299                 }
    300                 return true;
    301             }
    302         };
    303 
    304         // Populate the built-in wallpapers
    305         ArrayList<ResourceWallpaperInfo> wallpapers = findBundledWallpapers();
    306         mWallpapersView = (LinearLayout) findViewById(R.id.wallpaper_list);
    307         BuiltInWallpapersAdapter ia = new BuiltInWallpapersAdapter(this, wallpapers);
    308         populateWallpapersFromAdapter(mWallpapersView, ia, false, true);
    309 
    310         // Populate the saved wallpapers
    311         mSavedImages = new SavedWallpaperImages(this);
    312         mSavedImages.loadThumbnailsAndImageIdList();
    313         populateWallpapersFromAdapter(mWallpapersView, mSavedImages, true, true);
    314 
    315         // Populate the live wallpapers
    316         final LinearLayout liveWallpapersView =
    317                 (LinearLayout) findViewById(R.id.live_wallpaper_list);
    318         final LiveWallpaperListAdapter a = new LiveWallpaperListAdapter(this);
    319         a.registerDataSetObserver(new DataSetObserver() {
    320             public void onChanged() {
    321                 liveWallpapersView.removeAllViews();
    322                 populateWallpapersFromAdapter(liveWallpapersView, a, false, false);
    323                 initializeScrollForRtl();
    324                 updateTileIndices();
    325             }
    326         });
    327 
    328         // Populate the third-party wallpaper pickers
    329         final LinearLayout thirdPartyWallpapersView =
    330                 (LinearLayout) findViewById(R.id.third_party_wallpaper_list);
    331         final ThirdPartyWallpaperPickerListAdapter ta =
    332                 new ThirdPartyWallpaperPickerListAdapter(this);
    333         populateWallpapersFromAdapter(thirdPartyWallpapersView, ta, false, false);
    334 
    335         // Add a tile for the Gallery
    336         LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list);
    337         FrameLayout pickImageTile = (FrameLayout) getLayoutInflater().
    338                 inflate(R.layout.wallpaper_picker_image_picker_item, masterWallpaperList, false);
    339         setWallpaperItemPaddingToZero(pickImageTile);
    340         masterWallpaperList.addView(pickImageTile, 0);
    341 
    342         // Make its background the last photo taken on external storage
    343         Bitmap lastPhoto = getThumbnailOfLastPhoto();
    344         if (lastPhoto != null) {
    345             ImageView galleryThumbnailBg =
    346                     (ImageView) pickImageTile.findViewById(R.id.wallpaper_image);
    347             galleryThumbnailBg.setImageBitmap(getThumbnailOfLastPhoto());
    348             int colorOverlay = getResources().getColor(R.color.wallpaper_picker_translucent_gray);
    349             galleryThumbnailBg.setColorFilter(colorOverlay, PorterDuff.Mode.SRC_ATOP);
    350 
    351         }
    352 
    353         PickImageInfo pickImageInfo = new PickImageInfo();
    354         pickImageTile.setTag(pickImageInfo);
    355         pickImageInfo.setView(pickImageTile);
    356         pickImageTile.setOnClickListener(mThumbnailOnClickListener);
    357         pickImageInfo.setView(pickImageTile);
    358 
    359         updateTileIndices();
    360 
    361         // Update the scroll for RTL
    362         initializeScrollForRtl();
    363 
    364         // Create smooth layout transitions for when items are deleted
    365         final LayoutTransition transitioner = new LayoutTransition();
    366         transitioner.setDuration(200);
    367         transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
    368         transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
    369         mWallpapersView.setLayoutTransition(transitioner);
    370 
    371         // Action bar
    372         // Show the custom action bar view
    373         final ActionBar actionBar = getActionBar();
    374         actionBar.setCustomView(R.layout.actionbar_set_wallpaper);
    375         actionBar.getCustomView().setOnClickListener(
    376                 new View.OnClickListener() {
    377                     @Override
    378                     public void onClick(View v) {
    379                         if (mSelectedThumb != null) {
    380                             WallpaperTileInfo info = (WallpaperTileInfo) mSelectedThumb.getTag();
    381                             info.onSave(WallpaperPickerActivity.this);
    382                         }
    383                     }
    384                 });
    385 
    386         // CAB for deleting items
    387         mActionModeCallback = new ActionMode.Callback() {
    388             // Called when the action mode is created; startActionMode() was called
    389             @Override
    390             public boolean onCreateActionMode(ActionMode mode, Menu menu) {
    391                 // Inflate a menu resource providing context menu items
    392                 MenuInflater inflater = mode.getMenuInflater();
    393                 inflater.inflate(R.menu.cab_delete_wallpapers, menu);
    394                 return true;
    395             }
    396 
    397             private int numCheckedItems() {
    398                 int childCount = mWallpapersView.getChildCount();
    399                 int numCheckedItems = 0;
    400                 for (int i = 0; i < childCount; i++) {
    401                     CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
    402                     if (c.isChecked()) {
    403                         numCheckedItems++;
    404                     }
    405                 }
    406                 return numCheckedItems;
    407             }
    408 
    409             // Called each time the action mode is shown. Always called after onCreateActionMode,
    410             // but may be called multiple times if the mode is invalidated.
    411             @Override
    412             public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
    413                 int numCheckedItems = numCheckedItems();
    414                 if (numCheckedItems == 0) {
    415                     mode.finish();
    416                     return true;
    417                 } else {
    418                     mode.setTitle(getResources().getQuantityString(
    419                             R.plurals.number_of_items_selected, numCheckedItems, numCheckedItems));
    420                     return true;
    421                 }
    422             }
    423 
    424             // Called when the user selects a contextual menu item
    425             @Override
    426             public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
    427                 int itemId = item.getItemId();
    428                 if (itemId == R.id.menu_delete) {
    429                     int childCount = mWallpapersView.getChildCount();
    430                     ArrayList<View> viewsToRemove = new ArrayList<View>();
    431                     for (int i = 0; i < childCount; i++) {
    432                         CheckableFrameLayout c =
    433                                 (CheckableFrameLayout) mWallpapersView.getChildAt(i);
    434                         if (c.isChecked()) {
    435                             WallpaperTileInfo info = (WallpaperTileInfo) c.getTag();
    436                             info.onDelete(WallpaperPickerActivity.this);
    437                             viewsToRemove.add(c);
    438                         }
    439                     }
    440                     for (View v : viewsToRemove) {
    441                         mWallpapersView.removeView(v);
    442                     }
    443                     updateTileIndices();
    444                     mode.finish(); // Action picked, so close the CAB
    445                     return true;
    446                 } else {
    447                     return false;
    448                 }
    449             }
    450 
    451             // Called when the user exits the action mode
    452             @Override
    453             public void onDestroyActionMode(ActionMode mode) {
    454                 int childCount = mWallpapersView.getChildCount();
    455                 for (int i = 0; i < childCount; i++) {
    456                     CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
    457                     c.setChecked(false);
    458                 }
    459                 mSelectedThumb.setSelected(true);
    460                 mActionMode = null;
    461             }
    462         };
    463     }
    464 
    465     private void initializeScrollForRtl() {
    466         final HorizontalScrollView scroll =
    467                 (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container);
    468 
    469         if (scroll.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
    470             final ViewTreeObserver observer = scroll.getViewTreeObserver();
    471             observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    472                 public void onGlobalLayout() {
    473                     LinearLayout masterWallpaperList =
    474                             (LinearLayout) findViewById(R.id.master_wallpaper_list);
    475                     scroll.scrollTo(masterWallpaperList.getWidth(), 0);
    476                     scroll.getViewTreeObserver().removeOnGlobalLayoutListener(this);
    477                 }
    478             });
    479         }
    480     }
    481 
    482     public boolean enableRotation() {
    483         return super.enableRotation() || Launcher.sForceEnableRotation;
    484     }
    485 
    486     protected Bitmap getThumbnailOfLastPhoto() {
    487         Cursor cursor = MediaStore.Images.Media.query(getContentResolver(),
    488                 MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
    489                 new String[] { MediaStore.Images.ImageColumns._ID,
    490                     MediaStore.Images.ImageColumns.DATE_TAKEN},
    491                 null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC LIMIT 1");
    492         Bitmap thumb = null;
    493         if (cursor.moveToNext()) {
    494             int id = cursor.getInt(0);
    495             thumb = MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(),
    496                     id, MediaStore.Images.Thumbnails.MINI_KIND, null);
    497         }
    498         cursor.close();
    499         return thumb;
    500     }
    501 
    502     protected void onStop() {
    503         super.onStop();
    504         mWallpaperStrip = findViewById(R.id.wallpaper_strip);
    505         if (mWallpaperStrip.getAlpha() < 1f) {
    506             mWallpaperStrip.setAlpha(1f);
    507             mWallpaperStrip.setVisibility(View.VISIBLE);
    508         }
    509     }
    510 
    511     protected void onSaveInstanceState(Bundle outState) {
    512         outState.putParcelableArrayList(TEMP_WALLPAPER_TILES, mTempWallpaperTiles);
    513     }
    514 
    515     protected void onRestoreInstanceState(Bundle savedInstanceState) {
    516         ArrayList<Uri> uris = savedInstanceState.getParcelableArrayList(TEMP_WALLPAPER_TILES);
    517         for (Uri uri : uris) {
    518             addTemporaryWallpaperTile(uri);
    519         }
    520     }
    521 
    522     private void populateWallpapersFromAdapter(ViewGroup parent, BaseAdapter adapter,
    523             boolean addLongPressHandler, boolean selectFirstTile) {
    524         for (int i = 0; i < adapter.getCount(); i++) {
    525             FrameLayout thumbnail = (FrameLayout) adapter.getView(i, null, parent);
    526             parent.addView(thumbnail, i);
    527             WallpaperTileInfo info = (WallpaperTileInfo) adapter.getItem(i);
    528             thumbnail.setTag(info);
    529             info.setView(thumbnail);
    530             if (addLongPressHandler) {
    531                 addLongPressHandler(thumbnail);
    532             }
    533             thumbnail.setOnClickListener(mThumbnailOnClickListener);
    534             if (i == 0 && selectFirstTile) {
    535                 mThumbnailOnClickListener.onClick(thumbnail);
    536             }
    537         }
    538     }
    539 
    540     private void updateTileIndices() {
    541         LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list);
    542         final int childCount = masterWallpaperList.getChildCount();
    543         final Resources res = getResources();
    544 
    545         // Do two passes; the first pass gets the total number of tiles
    546         int numTiles = 0;
    547         for (int passNum = 0; passNum < 2; passNum++) {
    548             int tileIndex = 0;
    549             for (int i = 0; i < childCount; i++) {
    550                 View child = masterWallpaperList.getChildAt(i);
    551                 LinearLayout subList;
    552 
    553                 int subListStart;
    554                 int subListEnd;
    555                 if (child.getTag() instanceof WallpaperTileInfo) {
    556                     subList = masterWallpaperList;
    557                     subListStart = i;
    558                     subListEnd = i + 1;
    559                 } else { // if (child instanceof LinearLayout) {
    560                     subList = (LinearLayout) child;
    561                     subListStart = 0;
    562                     subListEnd = subList.getChildCount();
    563                 }
    564 
    565                 for (int j = subListStart; j < subListEnd; j++) {
    566                     WallpaperTileInfo info = (WallpaperTileInfo) subList.getChildAt(j).getTag();
    567                     if (info.isNamelessWallpaper()) {
    568                         if (passNum == 0) {
    569                             numTiles++;
    570                         } else {
    571                             CharSequence label = res.getString(
    572                                     R.string.wallpaper_accessibility_name, ++tileIndex, numTiles);
    573                             info.onIndexUpdated(label);
    574                         }
    575                     }
    576                 }
    577             }
    578         }
    579     }
    580 
    581     private static Point getDefaultThumbnailSize(Resources res) {
    582         return new Point(res.getDimensionPixelSize(R.dimen.wallpaperThumbnailWidth),
    583                 res.getDimensionPixelSize(R.dimen.wallpaperThumbnailHeight));
    584 
    585     }
    586 
    587     private static Bitmap createThumbnail(Point size, Context context, Uri uri, byte[] imageBytes,
    588             Resources res, int resId, int rotation, boolean leftAligned) {
    589         int width = size.x;
    590         int height = size.y;
    591 
    592         BitmapCropTask cropTask;
    593         if (uri != null) {
    594             cropTask = new BitmapCropTask(
    595                     context, uri, null, rotation, width, height, false, true, null);
    596         } else if (imageBytes != null) {
    597             cropTask = new BitmapCropTask(
    598                     imageBytes, null, rotation, width, height, false, true, null);
    599         }  else {
    600             cropTask = new BitmapCropTask(
    601                     context, res, resId, null, rotation, width, height, false, true, null);
    602         }
    603         Point bounds = cropTask.getImageBounds();
    604         if (bounds == null || bounds.x == 0 || bounds.y == 0) {
    605             return null;
    606         }
    607 
    608         Matrix rotateMatrix = new Matrix();
    609         rotateMatrix.setRotate(rotation);
    610         float[] rotatedBounds = new float[] { bounds.x, bounds.y };
    611         rotateMatrix.mapPoints(rotatedBounds);
    612         rotatedBounds[0] = Math.abs(rotatedBounds[0]);
    613         rotatedBounds[1] = Math.abs(rotatedBounds[1]);
    614 
    615         RectF cropRect = WallpaperCropActivity.getMaxCropRect(
    616                 (int) rotatedBounds[0], (int) rotatedBounds[1], width, height, leftAligned);
    617         cropTask.setCropBounds(cropRect);
    618 
    619         if (cropTask.cropBitmap()) {
    620             return cropTask.getCroppedBitmap();
    621         } else {
    622             return null;
    623         }
    624     }
    625 
    626     private void addTemporaryWallpaperTile(Uri uri) {
    627         mTempWallpaperTiles.add(uri);
    628         // Add a tile for the image picked from Gallery
    629         FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
    630                 inflate(R.layout.wallpaper_picker_item, mWallpapersView, false);
    631         setWallpaperItemPaddingToZero(pickedImageThumbnail);
    632 
    633         // Load the thumbnail
    634         ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
    635         Point defaultSize = getDefaultThumbnailSize(this.getResources());
    636         int rotation = WallpaperCropActivity.getRotationFromExif(this, uri);
    637         Bitmap thumb = createThumbnail(defaultSize, this, uri, null, null, 0, rotation, false);
    638         if (thumb != null) {
    639             image.setImageBitmap(thumb);
    640             Drawable thumbDrawable = image.getDrawable();
    641             thumbDrawable.setDither(true);
    642         } else {
    643             Log.e(TAG, "Error loading thumbnail for uri=" + uri);
    644         }
    645         mWallpapersView.addView(pickedImageThumbnail, 0);
    646 
    647         UriWallpaperInfo info = new UriWallpaperInfo(uri);
    648         pickedImageThumbnail.setTag(info);
    649         info.setView(pickedImageThumbnail);
    650         addLongPressHandler(pickedImageThumbnail);
    651         updateTileIndices();
    652         pickedImageThumbnail.setOnClickListener(mThumbnailOnClickListener);
    653         mThumbnailOnClickListener.onClick(pickedImageThumbnail);
    654     }
    655 
    656     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    657         if (requestCode == IMAGE_PICK && resultCode == RESULT_OK) {
    658             if (data != null && data.getData() != null) {
    659                 Uri uri = data.getData();
    660                 addTemporaryWallpaperTile(uri);
    661             }
    662         } else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY) {
    663             setResult(RESULT_OK);
    664             finish();
    665         } else if (requestCode == PICK_LIVE_WALLPAPER) {
    666             WallpaperManager wm = WallpaperManager.getInstance(this);
    667             final WallpaperInfo oldLiveWallpaper = mLiveWallpaperInfoOnPickerLaunch;
    668             WallpaperInfo newLiveWallpaper = wm.getWallpaperInfo();
    669             // Try to figure out if a live wallpaper was set;
    670             if (newLiveWallpaper != null &&
    671                     (oldLiveWallpaper == null ||
    672                     !oldLiveWallpaper.getComponent().equals(newLiveWallpaper.getComponent()))) {
    673                 // Return if a live wallpaper was set
    674                 setResult(RESULT_OK);
    675                 finish();
    676             }
    677         }
    678     }
    679 
    680     static void setWallpaperItemPaddingToZero(FrameLayout frameLayout) {
    681         frameLayout.setPadding(0, 0, 0, 0);
    682         frameLayout.setForeground(new ZeroPaddingDrawable(frameLayout.getForeground()));
    683     }
    684 
    685     private void addLongPressHandler(View v) {
    686         v.setOnLongClickListener(mLongClickListener);
    687     }
    688 
    689     private ArrayList<ResourceWallpaperInfo> findBundledWallpapers() {
    690         ArrayList<ResourceWallpaperInfo> bundledWallpapers =
    691                 new ArrayList<ResourceWallpaperInfo>(24);
    692 
    693         Pair<ApplicationInfo, Integer> r = getWallpaperArrayResourceId();
    694         if (r != null) {
    695             try {
    696                 Resources wallpaperRes = getPackageManager().getResourcesForApplication(r.first);
    697                 bundledWallpapers = addWallpapers(wallpaperRes, r.first.packageName, r.second);
    698             } catch (PackageManager.NameNotFoundException e) {
    699             }
    700         }
    701 
    702         // Add an entry for the default wallpaper (stored in system resources)
    703         ResourceWallpaperInfo defaultWallpaperInfo = getDefaultWallpaperInfo();
    704         if (defaultWallpaperInfo != null) {
    705             bundledWallpapers.add(0, defaultWallpaperInfo);
    706         }
    707         return bundledWallpapers;
    708     }
    709 
    710     private ResourceWallpaperInfo getDefaultWallpaperInfo() {
    711         Resources sysRes = Resources.getSystem();
    712         int resId = sysRes.getIdentifier("default_wallpaper", "drawable", "android");
    713 
    714         File defaultThumbFile = new File(getFilesDir(), "default_thumb.jpg");
    715         Bitmap thumb = null;
    716         boolean defaultWallpaperExists = false;
    717         if (defaultThumbFile.exists()) {
    718             thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath());
    719             defaultWallpaperExists = true;
    720         } else {
    721             Resources res = getResources();
    722             Point defaultThumbSize = getDefaultThumbnailSize(res);
    723             int rotation = WallpaperCropActivity.getRotationFromExif(res, resId);
    724             thumb = createThumbnail(
    725                     defaultThumbSize, this, null, null, sysRes, resId, rotation, false);
    726             if (thumb != null) {
    727                 try {
    728                     defaultThumbFile.createNewFile();
    729                     FileOutputStream thumbFileStream =
    730                             openFileOutput(defaultThumbFile.getName(), Context.MODE_PRIVATE);
    731                     thumb.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream);
    732                     thumbFileStream.close();
    733                     defaultWallpaperExists = true;
    734                 } catch (IOException e) {
    735                     Log.e(TAG, "Error while writing default wallpaper thumbnail to file " + e);
    736                     defaultThumbFile.delete();
    737                 }
    738             }
    739         }
    740         if (defaultWallpaperExists) {
    741             return new ResourceWallpaperInfo(sysRes, resId, new BitmapDrawable(thumb));
    742         }
    743         return null;
    744     }
    745 
    746     public Pair<ApplicationInfo, Integer> getWallpaperArrayResourceId() {
    747         // Context.getPackageName() may return the "original" package name,
    748         // com.android.launcher3; Resources needs the real package name,
    749         // com.android.launcher3. So we ask Resources for what it thinks the
    750         // package name should be.
    751         final String packageName = getResources().getResourcePackageName(R.array.wallpapers);
    752         try {
    753             ApplicationInfo info = getPackageManager().getApplicationInfo(packageName, 0);
    754             return new Pair<ApplicationInfo, Integer>(info, R.array.wallpapers);
    755         } catch (PackageManager.NameNotFoundException e) {
    756             return null;
    757         }
    758     }
    759 
    760     private ArrayList<ResourceWallpaperInfo> addWallpapers(
    761             Resources res, String packageName, int listResId) {
    762         ArrayList<ResourceWallpaperInfo> bundledWallpapers =
    763                 new ArrayList<ResourceWallpaperInfo>(24);
    764         final String[] extras = res.getStringArray(listResId);
    765         for (String extra : extras) {
    766             int resId = res.getIdentifier(extra, "drawable", packageName);
    767             if (resId != 0) {
    768                 final int thumbRes = res.getIdentifier(extra + "_small", "drawable", packageName);
    769 
    770                 if (thumbRes != 0) {
    771                     ResourceWallpaperInfo wallpaperInfo =
    772                             new ResourceWallpaperInfo(res, resId, res.getDrawable(thumbRes));
    773                     bundledWallpapers.add(wallpaperInfo);
    774                     // Log.d(TAG, "add: [" + packageName + "]: " + extra + " (" + res + ")");
    775                 }
    776             } else {
    777                 Log.e(TAG, "Couldn't find wallpaper " + extra);
    778             }
    779         }
    780         return bundledWallpapers;
    781     }
    782 
    783     public CropView getCropView() {
    784         return mCropView;
    785     }
    786 
    787     public SavedWallpaperImages getSavedImages() {
    788         return mSavedImages;
    789     }
    790 
    791     public void onLiveWallpaperPickerLaunch() {
    792         mLiveWallpaperInfoOnPickerLaunch = WallpaperManager.getInstance(this).getWallpaperInfo();
    793     }
    794 
    795     static class ZeroPaddingDrawable extends LevelListDrawable {
    796         public ZeroPaddingDrawable(Drawable d) {
    797             super();
    798             addLevel(0, 0, d);
    799             setLevel(0);
    800         }
    801 
    802         @Override
    803         public boolean getPadding(Rect padding) {
    804             padding.set(0, 0, 0, 0);
    805             return true;
    806         }
    807     }
    808 
    809     private static class BuiltInWallpapersAdapter extends BaseAdapter implements ListAdapter {
    810         private LayoutInflater mLayoutInflater;
    811         private ArrayList<ResourceWallpaperInfo> mWallpapers;
    812 
    813         BuiltInWallpapersAdapter(Activity activity, ArrayList<ResourceWallpaperInfo> wallpapers) {
    814             mLayoutInflater = activity.getLayoutInflater();
    815             mWallpapers = wallpapers;
    816         }
    817 
    818         public int getCount() {
    819             return mWallpapers.size();
    820         }
    821 
    822         public ResourceWallpaperInfo getItem(int position) {
    823             return mWallpapers.get(position);
    824         }
    825 
    826         public long getItemId(int position) {
    827             return position;
    828         }
    829 
    830         public View getView(int position, View convertView, ViewGroup parent) {
    831             Drawable thumb = mWallpapers.get(position).mThumb;
    832             if (thumb == null) {
    833                 Log.e(TAG, "Error decoding thumbnail for wallpaper #" + position);
    834             }
    835             return createImageTileView(mLayoutInflater, position, convertView, parent, thumb);
    836         }
    837     }
    838 
    839     public static View createImageTileView(LayoutInflater layoutInflater, int position,
    840             View convertView, ViewGroup parent, Drawable thumb) {
    841         View view;
    842 
    843         if (convertView == null) {
    844             view = layoutInflater.inflate(R.layout.wallpaper_picker_item, parent, false);
    845         } else {
    846             view = convertView;
    847         }
    848 
    849         setWallpaperItemPaddingToZero((FrameLayout) view);
    850 
    851         ImageView image = (ImageView) view.findViewById(R.id.wallpaper_image);
    852 
    853         if (thumb != null) {
    854             image.setImageDrawable(thumb);
    855             thumb.setDither(true);
    856         }
    857 
    858         return view;
    859     }
    860 }
    861