OSDN Git Service

Adding persistence of saved wallpaper images
authorMichael Jurka <mikejurka@google.com>
Tue, 10 Sep 2013 11:39:59 +0000 (13:39 +0200)
committerMichael Jurka <mikejurka@google.com>
Thu, 12 Sep 2013 18:48:53 +0000 (20:48 +0200)
Bug: 10640466
Bug: 10648660

Change-Id: I545a0419a3a6a23b75fb035d7a3be854426d13d4

src/com/android/launcher3/CropView.java
src/com/android/launcher3/SavedWallpaperImages.java [new file with mode: 0644]
src/com/android/launcher3/WallpaperCropActivity.java
src/com/android/launcher3/WallpaperPickerActivity.java

index 6d29be2..32c590d 100644 (file)
@@ -140,12 +140,12 @@ public class CropView extends TiledImageView implements OnScaleGestureListener {
     public void onScaleEnd(ScaleGestureDetector detector) {
     }
 
-    public void moveToUpperLeft() {
+    public void moveToLeft() {
         if (getWidth() == 0 || getHeight() == 0) {
             final ViewTreeObserver observer = getViewTreeObserver();
             observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                     public void onGlobalLayout() {
-                        moveToUpperLeft();
+                        moveToLeft();
                         getViewTreeObserver().removeOnGlobalLayoutListener(this);
                     }
                 });
@@ -154,7 +154,6 @@ public class CropView extends TiledImageView implements OnScaleGestureListener {
         getEdgesHelper(edges);
         final float scale = mRenderer.scale;
         mRenderer.centerX += Math.ceil(edges.left / scale);
-        mRenderer.centerY += Math.ceil(edges.top / scale);
     }
 
     public void setTouchEnabled(boolean enabled) {
diff --git a/src/com/android/launcher3/SavedWallpaperImages.java b/src/com/android/launcher3/SavedWallpaperImages.java
new file mode 100644 (file)
index 0000000..afeea2d
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+
+
+public class SavedWallpaperImages {
+    private static String TAG = "Launcher3.SavedWallpaperImages";
+    private ImageDb mDb;
+    ArrayList<Integer> mIds;
+    ArrayList<Drawable> mThumbs;
+    Context mContext;
+
+    public SavedWallpaperImages(Context context) {
+        mDb = new ImageDb(context);
+        mContext = context;
+    }
+
+    public void loadThumbnailsAndImageIdList() {
+        mIds = new ArrayList<Integer>();
+        mThumbs = new ArrayList<Drawable>();
+        SQLiteDatabase db = mDb.getReadableDatabase();
+        Cursor result = db.query(ImageDb.TABLE_NAME,
+                new String[] { ImageDb.COLUMN_ID,
+                    ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME }, // cols to return
+                null, // select query
+                null, // args to select query
+                null,
+                null,
+                null,
+                null);
+
+        while (result.moveToNext()) {
+            String filename = result.getString(1);
+            File file = new File(mContext.getFilesDir(), filename);
+            Bitmap thumb = BitmapFactory.decodeFile(file.getAbsolutePath());
+            if (thumb != null) {
+                mIds.add(result.getInt(0));
+                mThumbs.add(new BitmapDrawable(thumb));
+            }
+        }
+        result.close();
+    }
+
+    public ArrayList<Drawable> getThumbnails() {
+        return mThumbs;
+    }
+
+    public ArrayList<Integer> getImageIds() {
+        return mIds;
+    }
+
+    public String getImageFilename(int id) {
+        Pair<String, String> filenames = getImageFilenames(id);
+        if (filenames != null) {
+            return filenames.second;
+        }
+        return null;
+    }
+
+    private Pair<String, String> getImageFilenames(int id) {
+        SQLiteDatabase db = mDb.getReadableDatabase();
+        Cursor result = db.query(ImageDb.TABLE_NAME,
+                new String[] { ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME,
+                    ImageDb.COLUMN_IMAGE_FILENAME }, // cols to return
+                ImageDb.COLUMN_ID + " = ?", // select query
+                new String[] { Integer.toString(id) }, // args to select query
+                null,
+                null,
+                null,
+                null);
+        if (result.getCount() > 0) {
+            result.moveToFirst();
+            String thumbFilename = result.getString(0);
+            String imageFilename = result.getString(1);
+            result.close();
+            return new Pair<String, String>(thumbFilename, imageFilename);
+        } else {
+            return null;
+        }
+    }
+
+    public void deleteImage(int id) {
+        Pair<String, String> filenames = getImageFilenames(id);
+        File imageFile = new File(mContext.getFilesDir(), filenames.first);
+        imageFile.delete();
+        File thumbFile = new File(mContext.getFilesDir(), filenames.second);
+        thumbFile.delete();
+        SQLiteDatabase db = mDb.getWritableDatabase();
+        db.delete(ImageDb.TABLE_NAME,
+                ImageDb.COLUMN_ID + " = ?", // SELECT query
+                new String[] {
+                    Integer.toString(id) // args to SELECT query
+                });
+    }
+
+    public void writeImage(Bitmap thumbnail, byte[] imageBytes) {
+        try {
+            File imageFile = File.createTempFile("wallpaper", "", mContext.getFilesDir());
+            FileOutputStream imageFileStream =
+                    mContext.openFileOutput(imageFile.getName(), Context.MODE_PRIVATE);
+            imageFileStream.write(imageBytes);
+            imageFileStream.close();
+
+            ByteArrayOutputStream stream = new ByteArrayOutputStream();
+            thumbnail.compress(Bitmap.CompressFormat.JPEG, 95, stream);
+            File thumbFile = File.createTempFile("wallpaperthumb", "", mContext.getFilesDir());
+            FileOutputStream thumbFileStream =
+                    mContext.openFileOutput(thumbFile.getName(), Context.MODE_PRIVATE);
+            thumbFileStream.write(stream.toByteArray());
+
+            SQLiteDatabase db = mDb.getWritableDatabase();
+            ContentValues values = new ContentValues();
+            values.put(ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME, thumbFile.getName());
+            values.put(ImageDb.COLUMN_IMAGE_FILENAME, imageFile.getName());
+            db.insert(ImageDb.TABLE_NAME, null, values);
+        } catch (IOException e) {
+            Log.e(TAG, "Failed writing images to storage " + e);
+        }
+    }
+
+    static class ImageDb extends SQLiteOpenHelper {
+        final static int DB_VERSION = 1;
+        final static String DB_NAME = "saved_wallpaper_images.db";
+        final static String TABLE_NAME = "saved_wallpaper_images";
+        final static String COLUMN_ID = "id";
+        final static String COLUMN_IMAGE_THUMBNAIL_FILENAME = "image_thumbnail";
+        final static String COLUMN_IMAGE_FILENAME = "image";
+
+        Context mContext;
+
+        public ImageDb(Context context) {
+            super(context, new File(context.getCacheDir(), DB_NAME).getPath(), null, DB_VERSION);
+            // Store the context for later use
+            mContext = context;
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase database) {
+            database.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
+                    COLUMN_ID + " INTEGER NOT NULL, " +
+                    COLUMN_IMAGE_THUMBNAIL_FILENAME + " TEXT NOT NULL, " +
+                    COLUMN_IMAGE_FILENAME + " TEXT NOT NULL, " +
+                    "PRIMARY KEY (" + COLUMN_ID + " ASC) " +
+                    ");");
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            if (oldVersion != newVersion) {
+                // Delete all the records; they'll be repopulated as this is a cache
+                db.execSQL("DELETE FROM " + TABLE_NAME);
+            }
+        }
+    }
+}
index 6832b0c..dee9fe9 100644 (file)
@@ -95,7 +95,7 @@ public class WallpaperCropActivity extends Activity {
                     @Override
                     public void onClick(View v) {
                         boolean finishActivityWhenDone = true;
-                        cropImageAndSetWallpaper(imageUri, finishActivityWhenDone);
+                        cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
                     }
                 });
     }
@@ -155,15 +155,34 @@ public class WallpaperCropActivity extends Activity {
         return new Point(defaultWidth, defaultHeight);
     }
 
-    protected void cropImageAndSetWallpaper(Resources res,
-            int resId, final boolean finishActivityWhenDone) {
+    protected void setWallpaper(String filePath, final boolean finishActivityWhenDone) {
+
+        BitmapCropTask cropTask = new BitmapCropTask(this,
+                filePath, null, 0, 0, true, false, null);
+        final Point bounds = cropTask.getImageBounds();
+        Runnable onEndCrop = new Runnable() {
+            public void run() {
+                updateWallpaperDimensions(bounds.x, bounds.y);
+                if (finishActivityWhenDone) {
+                    setResult(Activity.RESULT_OK);
+                    finish();
+                }
+            }
+        };
+        cropTask.setOnEndRunnable(onEndCrop);
+        cropTask.setNoCrop(true);
+        cropTask.execute();
+    }
+
+    protected void cropImageAndSetWallpaper(
+            Resources res, int resId, final boolean finishActivityWhenDone) {
         // crop this image and scale it down to the default wallpaper size for
         // this device
         Point inSize = mCropView.getSourceDimensions();
         Point outSize = getDefaultWallpaperSize(getResources(),
                 getWindowManager());
         RectF crop = getMaxCropRect(
-                inSize.x, inSize.y, outSize.x, outSize.y);
+                inSize.x, inSize.y, outSize.x, outSize.y, false);
         Runnable onEndCrop = new Runnable() {
             public void run() {
                 // Passing 0, 0 will cause launcher to revert to using the
@@ -181,7 +200,8 @@ public class WallpaperCropActivity extends Activity {
         cropTask.execute();
     }
 
-    protected void cropImageAndSetWallpaper(Uri uri, final boolean finishActivityWhenDone) {
+    protected void cropImageAndSetWallpaper(Uri uri,
+            OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) {
      // Get the crop
         Point inSize = mCropView.getSourceDimensions();
 
@@ -258,11 +278,21 @@ public class WallpaperCropActivity extends Activity {
         };
         BitmapCropTask cropTask = new BitmapCropTask(uri,
                 cropRect, outWidth, outHeight, true, false, onEndCrop);
+        if (onBitmapCroppedHandler != null) {
+            cropTask.setOnBitmapCropped(onBitmapCroppedHandler);
+        }
         cropTask.execute();
     }
 
+    public interface OnBitmapCroppedHandler {
+        public void onBitmapCropped(byte[] imageBytes);
+    }
+
     protected class BitmapCropTask extends AsyncTask<Void, Void, Boolean> {
         Uri mInUri = null;
+        Context mContext;
+        String mInFilePath;
+        byte[] mInImageBytes;
         int mInResId = 0;
         InputStream mInStream;
         RectF mCropBounds = null;
@@ -275,44 +305,81 @@ public class WallpaperCropActivity extends Activity {
         Bitmap mCroppedBitmap;
         Runnable mOnEndRunnable;
         Resources mResources;
+        OnBitmapCroppedHandler mOnBitmapCroppedHandler;
+        boolean mNoCrop;
+
+        public BitmapCropTask(Context c, String filePath,
+                RectF cropBounds, int outWidth, int outHeight,
+                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
+            mContext = c;
+            mInFilePath = filePath;
+            mWPManager = WallpaperManager.getInstance(getApplicationContext());
+            init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
+        }
+
+        public BitmapCropTask(byte[] imageBytes,
+                RectF cropBounds, int outWidth, int outHeight,
+                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
+            mInImageBytes = imageBytes;
+            mWPManager = WallpaperManager.getInstance(getApplicationContext());
+            init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
+        }
 
         public BitmapCropTask(Uri inUri,
                 RectF cropBounds, int outWidth, int outHeight,
                 boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
             mInUri = inUri;
-            mCropBounds = cropBounds;
-            mOutWidth = outWidth;
-            mOutHeight = outHeight;
             mWPManager = WallpaperManager.getInstance(getApplicationContext());
-            mSetWallpaper = setWallpaper;
-            mSaveCroppedBitmap = saveCroppedBitmap;
-            mOnEndRunnable = onEndRunnable;
+            init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
         }
 
         public BitmapCropTask(Resources res, int inResId,
                 RectF cropBounds, int outWidth, int outHeight,
                 boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
             mInResId = inResId;
+            mResources = res;
+            mWPManager = WallpaperManager.getInstance(getApplicationContext());
+            init(cropBounds, outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
+        }
+
+        private void init(RectF cropBounds, int outWidth, int outHeight,
+                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
             mCropBounds = cropBounds;
             mOutWidth = outWidth;
             mOutHeight = outHeight;
-            mWPManager = WallpaperManager.getInstance(getApplicationContext());
             mSetWallpaper = setWallpaper;
             mSaveCroppedBitmap = saveCroppedBitmap;
             mOnEndRunnable = onEndRunnable;
-            mResources = res;
+        }
+
+        public void setOnBitmapCropped(OnBitmapCroppedHandler handler) {
+            mOnBitmapCroppedHandler = handler;
+        }
+
+        public void setNoCrop(boolean value) {
+            mNoCrop = value;
+        }
+
+        public void setOnEndRunnable(Runnable onEndRunnable) {
+            mOnEndRunnable = onEndRunnable;
         }
 
         // Helper to setup input stream
         private void regenerateInputStream() {
-            if (mInUri == null && mInResId == 0) {
-                Log.w(LOGTAG, "cannot read original file, no input URI or resource ID given");
+            if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) {
+                Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " +
+                        "image byte array given");
             } else {
                 Utils.closeSilently(mInStream);
                 try {
                     if (mInUri != null) {
                         mInStream = new BufferedInputStream(
                                 getContentResolver().openInputStream(mInUri));
+                    } else if (mInFilePath != null) {
+                        mInStream = mContext.openFileInput(mInFilePath);
+                    } else if (mInImageBytes != null) {
+                        mInStream = new BufferedInputStream(
+                                new ByteArrayInputStream(mInImageBytes));
                     } else {
                         mInStream = new BufferedInputStream(
                                 mResources.openRawResource(mInResId));
@@ -348,6 +415,18 @@ public class WallpaperCropActivity extends Activity {
 
             regenerateInputStream();
 
+            if (mNoCrop && mInStream != null) {
+                try {
+                    mWPManager.setStream(mInStream);
+                } catch (IOException e) {
+                    Log.w(LOGTAG, "cannot write stream to wallpaper", e);
+                    failure = true;
+                }
+                if (mOnEndRunnable != null) {
+                    mOnEndRunnable.run();
+                }
+                return !failure;
+            }
             if (mInStream != null) {
                 // Find crop bounds (scaled to original image size)
                 Rect roundedTrueCrop = new Rect();
@@ -450,8 +529,11 @@ public class WallpaperCropActivity extends Activity {
                             failure = true;
                         } else {
                             try {
-                                mWPManager.setStream(new ByteArrayInputStream(tmpOut
-                                        .toByteArray()));
+                                byte[] outByteArray = tmpOut.toByteArray();
+                                mWPManager.setStream(new ByteArrayInputStream(outByteArray));
+                                if (mOnBitmapCroppedHandler != null) {
+                                    mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
+                                }
                             } catch (IOException e) {
                                 Log.w(LOGTAG, "cannot write stream to wallpaper", e);
                                 failure = true;
@@ -516,7 +598,8 @@ public class WallpaperCropActivity extends Activity {
     }
 
 
-    protected static RectF getMaxCropRect(int inWidth, int inHeight, int outWidth, int outHeight) {
+    protected static RectF getMaxCropRect(
+            int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) {
         RectF cropRect = new RectF();
         // Get a crop rect that will fit this
         if (inWidth / (float) inHeight > outWidth / (float) outHeight) {
@@ -524,6 +607,10 @@ public class WallpaperCropActivity extends Activity {
              cropRect.bottom = inHeight;
              cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2;
              cropRect.right = inWidth - cropRect.left;
+             if (leftAligned) {
+                 cropRect.right -= cropRect.left;
+                 cropRect.left = 0;
+             }
         } else {
             cropRect.left = 0;
             cropRect.right = inWidth;
index ec0106f..59318d9 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import android.animation.LayoutTransition;
 import android.app.ActionBar;
 import android.app.Activity;
 import android.content.ComponentName;
@@ -25,6 +26,7 @@ import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -51,26 +53,34 @@ import android.widget.TextView;
 
 import com.android.photos.BitmapRegionTileSource;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 
 public class WallpaperPickerActivity extends WallpaperCropActivity {
-    private static final String TAG = "Launcher.WallpaperPickerActivity";
+    static final String TAG = "Launcher.WallpaperPickerActivity";
 
     private static final int IMAGE_PICK = 5;
     private static final int PICK_WALLPAPER_THIRD_PARTY_ACTIVITY = 6;
 
-    private ArrayList<Drawable> mThumbs;
-    private ArrayList<Integer> mImages;
+    private ArrayList<Drawable> mBundledWallpaperThumbs;
+    private ArrayList<Integer> mBundledWallpaperResIds;
     private Resources mWallpaperResources;
 
     private View mSelectedThumb;
     private boolean mIgnoreNextTap;
     private OnClickListener mThumbnailOnClickListener;
 
+    private LinearLayout mWallpapersView;
+    private View.OnLongClickListener mLongClickListener;
+
+    ArrayList<Uri> mTempWallpaperTiles = new ArrayList<Uri>();
+    private SavedWallpaperImages mSavedImages;
+
     private static class ThumbnailMetaData {
         public boolean mLaunchesGallery;
-        public Uri mGalleryImageUri;
+        public Uri mWallpaperUri;
+        public int mSavedWallpaperDbId;
         public int mWallpaperResId;
     }
 
@@ -126,11 +136,18 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
                     intent.setType("image/*");
                     Utilities.startActivityForResultSafely(
                             WallpaperPickerActivity.this, intent, IMAGE_PICK);
-                } else if (meta.mGalleryImageUri != null) {
+                } else if (meta.mWallpaperUri != null) {
                     mCropView.setTileSource(new BitmapRegionTileSource(WallpaperPickerActivity.this,
-                            meta.mGalleryImageUri, 1024, 0), null);
+                            meta.mWallpaperUri, 1024, 0), null);
                     mCropView.setTouchEnabled(true);
-                } else {
+                } else if (meta.mSavedWallpaperDbId != 0) {
+                    String imageFilename = mSavedImages.getImageFilename(meta.mSavedWallpaperDbId);
+                    File file = new File(getFilesDir(), imageFilename);
+                    mCropView.setTileSource(new BitmapRegionTileSource(WallpaperPickerActivity.this,
+                            file.getAbsolutePath(), 1024, 0), null);
+                    mCropView.moveToLeft();
+                    mCropView.setTouchEnabled(false);
+                } else if (meta.mWallpaperResId != 0) {
                     BitmapRegionTileSource source = new BitmapRegionTileSource(mWallpaperResources,
                             WallpaperPickerActivity.this, meta.mWallpaperResId, 1024, 0);
                     mCropView.setTileSource(source, null);
@@ -138,7 +155,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
                             getResources(), getWindowManager());
                     RectF crop = WallpaperCropActivity.getMaxCropRect(
                             source.getImageWidth(), source.getImageHeight(),
-                            wallpaperSize.x, wallpaperSize.y);
+                            wallpaperSize.x, wallpaperSize.y, false);
                     mCropView.setScale(wallpaperSize.x / crop.width());
                     mCropView.setTouchEnabled(false);
                 }
@@ -146,31 +163,28 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
         };
 
         // Populate the built-in wallpapers
-        findWallpapers();
+        findBundledWallpapers();
+        mWallpapersView = (LinearLayout) findViewById(R.id.wallpaper_list);
+        ImageAdapter ia = new ImageAdapter(this, mBundledWallpaperThumbs);
+        populateWallpapersFromAdapter(mWallpapersView, ia, mBundledWallpaperResIds, true, false);
+
+        // Populate the saved wallpapers
+        mSavedImages = new SavedWallpaperImages(this);
+        mSavedImages.loadThumbnailsAndImageIdList();
+        ArrayList<Drawable> savedWallpaperThumbs = mSavedImages.getThumbnails();
+        ArrayList<Integer > savedWallpaperIds = mSavedImages.getImageIds();
+        ia = new ImageAdapter(this, savedWallpaperThumbs);
+        populateWallpapersFromAdapter(mWallpapersView, ia, savedWallpaperIds, false, true);
 
-        LinearLayout wallpapers = (LinearLayout) findViewById(R.id.wallpaper_list);
-        ImageAdapter ia = new ImageAdapter(this);
-        for (int i = 0; i < ia.getCount(); i++) {
-            FrameLayout thumbnail = (FrameLayout) ia.getView(i, null, wallpapers);
-            wallpapers.addView(thumbnail, i);
-
-            ThumbnailMetaData meta = new ThumbnailMetaData();
-            meta.mWallpaperResId = mImages.get(i);
-            thumbnail.setTag(meta);
-            thumbnail.setOnClickListener(mThumbnailOnClickListener);
-            if (i == 0) {
-                mThumbnailOnClickListener.onClick(thumbnail);
-            }
-        }
         // Add a tile for the Gallery
         FrameLayout galleryThumbnail = (FrameLayout) getLayoutInflater().
-                inflate(R.layout.wallpaper_picker_gallery_item, wallpapers, false);
+                inflate(R.layout.wallpaper_picker_gallery_item, mWallpapersView, false);
         setWallpaperItemPaddingToZero(galleryThumbnail);
 
         TextView galleryLabel =
                 (TextView) galleryThumbnail.findViewById(R.id.wallpaper_item_label);
         galleryLabel.setText(R.string.gallery);
-        wallpapers.addView(galleryThumbnail, 0);
+        mWallpapersView.addView(galleryThumbnail, 0);
 
         ThumbnailMetaData meta = new ThumbnailMetaData();
         meta.mLaunchesGallery = true;
@@ -188,9 +202,20 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
                         ThumbnailMetaData meta = (ThumbnailMetaData) mSelectedThumb.getTag();
                         if (meta.mLaunchesGallery) {
                             // shouldn't be selected, but do nothing
-                        } else if (meta.mGalleryImageUri != null) {
+                        } else if (meta.mWallpaperUri != null) {
+                            boolean finishActivityWhenDone = true;
+                            OnBitmapCroppedHandler h = new OnBitmapCroppedHandler() {
+                                public void onBitmapCropped(byte[] imageBytes) {
+                                    Bitmap thumb = createThumbnail(null, imageBytes, true);
+                                    mSavedImages.writeImage(thumb, imageBytes);
+                                }
+                            };
+                            cropImageAndSetWallpaper(meta.mWallpaperUri, h, finishActivityWhenDone);
+                        } else if (meta.mSavedWallpaperDbId != 0) {
                             boolean finishActivityWhenDone = true;
-                            cropImageAndSetWallpaper(meta.mGalleryImageUri, finishActivityWhenDone);
+                            String imageFilename =
+                                    mSavedImages.getImageFilename(meta.mSavedWallpaperDbId);
+                            setWallpaper(imageFilename, finishActivityWhenDone);
                         } else if (meta.mWallpaperResId != 0) {
                             boolean finishActivityWhenDone = true;
                             cropImageAndSetWallpaper(mWallpaperResources,
@@ -200,45 +225,91 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
                 });
     }
 
-    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == IMAGE_PICK && resultCode == RESULT_OK) {
-            Uri uri = data.getData();
 
-            // Add a tile for the image picked from Gallery
-            LinearLayout wallpapers = (LinearLayout) findViewById(R.id.wallpaper_list);
-            FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
-                    inflate(R.layout.wallpaper_picker_item, wallpapers, false);
-            setWallpaperItemPaddingToZero(pickedImageThumbnail);
 
-            // Load the thumbnail
-            ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
 
-            Resources res = getResources();
-            int width = res.getDimensionPixelSize(R.dimen.wallpaperThumbnailWidth);
-            int height = res.getDimensionPixelSize(R.dimen.wallpaperThumbnailHeight);
 
-            BitmapCropTask cropTask =
-                    new BitmapCropTask(uri, null, width, height, false, true, null);
-            Point bounds = cropTask.getImageBounds();
 
-            RectF cropRect = WallpaperCropActivity.getMaxCropRect(
-                    bounds.x, bounds.y, width, height);
-            cropTask.setCropBounds(cropRect);
+    private void populateWallpapersFromAdapter(ViewGroup parent, ImageAdapter ia,
+            ArrayList<Integer> imageIds, boolean imagesAreResources, boolean addLongPressHandler) {
+        for (int i = 0; i < ia.getCount(); i++) {
+            FrameLayout thumbnail = (FrameLayout) ia.getView(i, null, parent);
+            parent.addView(thumbnail, i);
 
-            if (cropTask.cropBitmap()) {
-                image.setImageBitmap(cropTask.getCroppedBitmap());
-                Drawable thumbDrawable = image.getDrawable();
-                thumbDrawable.setDither(true);
+            ThumbnailMetaData meta = new ThumbnailMetaData();
+            if (imagesAreResources) {
+                meta.mWallpaperResId = imageIds.get(i);
             } else {
-                Log.e(TAG, "Error loading thumbnail for uri=" + uri);
+                meta.mSavedWallpaperDbId = imageIds.get(i);
             }
-            wallpapers.addView(pickedImageThumbnail, 0);
+            thumbnail.setTag(meta);
+            if (addLongPressHandler) {
+                addLongPressHandler(thumbnail);
+            }
+            thumbnail.setOnClickListener(mThumbnailOnClickListener);
+            if (i == 0) {
+                mThumbnailOnClickListener.onClick(thumbnail);
+            }
+        }
+    }
 
-            ThumbnailMetaData meta = new ThumbnailMetaData();
-            meta.mGalleryImageUri = uri;
-            pickedImageThumbnail.setTag(meta);
-            pickedImageThumbnail.setOnClickListener(mThumbnailOnClickListener);
-            mThumbnailOnClickListener.onClick(pickedImageThumbnail);
+    private Bitmap createThumbnail(Uri uri, byte[] imageBytes, boolean leftAligned) {
+        Resources res = getResources();
+        int width = res.getDimensionPixelSize(R.dimen.wallpaperThumbnailWidth);
+        int height = res.getDimensionPixelSize(R.dimen.wallpaperThumbnailHeight);
+
+        BitmapCropTask cropTask;
+        if (uri != null) {
+            cropTask = new BitmapCropTask(uri, null, width, height, false, true, null);
+        } else {
+            cropTask = new BitmapCropTask(imageBytes, null, width, height, false, true, null);
+        }
+        Point bounds = cropTask.getImageBounds();
+        if (bounds == null) {
+            return null;
+        }
+
+        RectF cropRect = WallpaperCropActivity.getMaxCropRect(
+                bounds.x, bounds.y, width, height, leftAligned);
+        cropTask.setCropBounds(cropRect);
+
+        if (cropTask.cropBitmap()) {
+            return cropTask.getCroppedBitmap();
+        } else {
+            return null;
+        }
+    }
+
+    private void addTemporaryWallpaperTile(Uri uri) {
+        mTempWallpaperTiles.add(uri);
+        // Add a tile for the image picked from Gallery
+        FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
+                inflate(R.layout.wallpaper_picker_item, mWallpapersView, false);
+        setWallpaperItemPaddingToZero(pickedImageThumbnail);
+
+        // Load the thumbnail
+        ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
+        Bitmap thumb = createThumbnail(uri, null, false);
+        if (thumb != null) {
+            image.setImageBitmap(thumb);
+            Drawable thumbDrawable = image.getDrawable();
+            thumbDrawable.setDither(true);
+        } else {
+            Log.e(TAG, "Error loading thumbnail for uri=" + uri);
+        }
+        mWallpapersView.addView(pickedImageThumbnail, 1);
+
+        ThumbnailMetaData meta = new ThumbnailMetaData();
+        meta.mWallpaperUri = uri;
+        pickedImageThumbnail.setTag(meta);
+        pickedImageThumbnail.setOnClickListener(mThumbnailOnClickListener);
+        mThumbnailOnClickListener.onClick(pickedImageThumbnail);
+    }
+
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (requestCode == IMAGE_PICK && resultCode == RESULT_OK) {
+            Uri uri = data.getData();
+            addTemporaryWallpaperTile(uri);
         } else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY) {
             // No result code is returned; just return
             setResult(RESULT_OK);
@@ -251,6 +322,11 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
         frameLayout.setForeground(new ZeroPaddingDrawable(frameLayout.getForeground()));
     }
 
+    private void addLongPressHandler(View v) {
+        v.setOnLongClickListener(mLongClickListener);
+    }
+
+
     public boolean onMenuItemSelected(int featureId, MenuItem item) {
         if (item.getIntent() == null) {
             return super.onMenuItemSelected(featureId, item);
@@ -312,9 +388,9 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
         return super.onCreateOptionsMenu(menu);
     }
 
-    private void findWallpapers() {
-        mThumbs = new ArrayList<Drawable>(24);
-        mImages = new ArrayList<Integer>(24);
+    private void findBundledWallpapers() {
+        mBundledWallpaperThumbs = new ArrayList<Drawable>(24);
+        mBundledWallpaperResIds = new ArrayList<Integer>(24);
 
         Pair<ApplicationInfo, Integer> r = getWallpaperArrayResourceId();
         if (r != null) {
@@ -349,8 +425,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
                         "drawable", packageName);
 
                 if (thumbRes != 0) {
-                    mThumbs.add(resources.getDrawable(thumbRes));
-                    mImages.add(res);
+                    mBundledWallpaperThumbs.add(resources.getDrawable(thumbRes));
+                    mBundledWallpaperResIds.add(res);
                     // Log.d(TAG, "add: [" + packageName + "]: " + extra + " (" + res + ")");
                 }
             } else {
@@ -373,11 +449,13 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
         }
     }
 
-    private class ImageAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter {
+    private static class ImageAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter {
         private LayoutInflater mLayoutInflater;
+        private ArrayList<Drawable> mThumbs;
 
-        ImageAdapter(Activity activity) {
+        ImageAdapter(Activity activity, ArrayList<Drawable> thumbs) {
             mLayoutInflater = activity.getLayoutInflater();
+            mThumbs = thumbs;
         }
 
         public int getCount() {