Improve wallpaper cropping

- Add support for tablets
- Add support for parallax with user-cropped images
- Improve behavior in landscape

Change-Id: I50c4bba59b03d26d595a086a9ea425894f341705
diff --git a/res/drawable-nodpi/wallpaper_01.jpg b/res/drawable-nodpi/wallpaper_01.jpg
index 1a07f2e..6c4299c 100644
--- a/res/drawable-nodpi/wallpaper_01.jpg
+++ b/res/drawable-nodpi/wallpaper_01.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_01_small.jpg b/res/drawable-nodpi/wallpaper_01_small.jpg
index 8965ee5..96482b8 100644
--- a/res/drawable-nodpi/wallpaper_01_small.jpg
+++ b/res/drawable-nodpi/wallpaper_01_small.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_02.jpg b/res/drawable-nodpi/wallpaper_02.jpg
index 3d10150..c905f73 100644
--- a/res/drawable-nodpi/wallpaper_02.jpg
+++ b/res/drawable-nodpi/wallpaper_02.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_02_small.jpg b/res/drawable-nodpi/wallpaper_02_small.jpg
index 2160b5e..3c6fccf 100644
--- a/res/drawable-nodpi/wallpaper_02_small.jpg
+++ b/res/drawable-nodpi/wallpaper_02_small.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_03.jpg b/res/drawable-nodpi/wallpaper_03.jpg
index f29376f..56d20bd 100644
--- a/res/drawable-nodpi/wallpaper_03.jpg
+++ b/res/drawable-nodpi/wallpaper_03.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_03_small.jpg b/res/drawable-nodpi/wallpaper_03_small.jpg
index 38ee82c..bb4c3b4 100644
--- a/res/drawable-nodpi/wallpaper_03_small.jpg
+++ b/res/drawable-nodpi/wallpaper_03_small.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_04.jpg b/res/drawable-nodpi/wallpaper_04.jpg
index 254daf0..3a3e3b0 100644
--- a/res/drawable-nodpi/wallpaper_04.jpg
+++ b/res/drawable-nodpi/wallpaper_04.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_04_small.jpg b/res/drawable-nodpi/wallpaper_04_small.jpg
index bf6471c..6478c67 100644
--- a/res/drawable-nodpi/wallpaper_04_small.jpg
+++ b/res/drawable-nodpi/wallpaper_04_small.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_05.jpg b/res/drawable-nodpi/wallpaper_05.jpg
index 9751dc4..dec9b56 100644
--- a/res/drawable-nodpi/wallpaper_05.jpg
+++ b/res/drawable-nodpi/wallpaper_05.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_05_small.jpg b/res/drawable-nodpi/wallpaper_05_small.jpg
index 5cfe680..ab359e2 100644
--- a/res/drawable-nodpi/wallpaper_05_small.jpg
+++ b/res/drawable-nodpi/wallpaper_05_small.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_06.jpg b/res/drawable-nodpi/wallpaper_06.jpg
index 6f2812d..fd5bbdd 100644
--- a/res/drawable-nodpi/wallpaper_06.jpg
+++ b/res/drawable-nodpi/wallpaper_06.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_06_small.jpg b/res/drawable-nodpi/wallpaper_06_small.jpg
index 6ff2254..0ccd4bb 100644
--- a/res/drawable-nodpi/wallpaper_06_small.jpg
+++ b/res/drawable-nodpi/wallpaper_06_small.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_07.jpg b/res/drawable-nodpi/wallpaper_07.jpg
index dc01b22..fa72f92 100644
--- a/res/drawable-nodpi/wallpaper_07.jpg
+++ b/res/drawable-nodpi/wallpaper_07.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_07_small.jpg b/res/drawable-nodpi/wallpaper_07_small.jpg
index 9a32752..64ab7f6 100644
--- a/res/drawable-nodpi/wallpaper_07_small.jpg
+++ b/res/drawable-nodpi/wallpaper_07_small.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_08.jpg b/res/drawable-nodpi/wallpaper_08.jpg
index e142ca0..9178e13 100644
--- a/res/drawable-nodpi/wallpaper_08.jpg
+++ b/res/drawable-nodpi/wallpaper_08.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_08_small.jpg b/res/drawable-nodpi/wallpaper_08_small.jpg
index 76c3f7f..9cbeddb 100644
--- a/res/drawable-nodpi/wallpaper_08_small.jpg
+++ b/res/drawable-nodpi/wallpaper_08_small.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_09.jpg b/res/drawable-nodpi/wallpaper_09.jpg
index 1771739..0016983 100644
--- a/res/drawable-nodpi/wallpaper_09.jpg
+++ b/res/drawable-nodpi/wallpaper_09.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_09_small.jpg b/res/drawable-nodpi/wallpaper_09_small.jpg
index da3fbf7..6cc15b5 100644
--- a/res/drawable-nodpi/wallpaper_09_small.jpg
+++ b/res/drawable-nodpi/wallpaper_09_small.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_10.jpg b/res/drawable-nodpi/wallpaper_10.jpg
index 4d9815a..60976e1 100644
--- a/res/drawable-nodpi/wallpaper_10.jpg
+++ b/res/drawable-nodpi/wallpaper_10.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_10_small.jpg b/res/drawable-nodpi/wallpaper_10_small.jpg
index c33eafa..3826672 100644
--- a/res/drawable-nodpi/wallpaper_10_small.jpg
+++ b/res/drawable-nodpi/wallpaper_10_small.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_11.jpg b/res/drawable-nodpi/wallpaper_11.jpg
index 46a4aa3..ac1e677 100644
--- a/res/drawable-nodpi/wallpaper_11.jpg
+++ b/res/drawable-nodpi/wallpaper_11.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_11_small.jpg b/res/drawable-nodpi/wallpaper_11_small.jpg
index 5107c0c..d3c0f53 100644
--- a/res/drawable-nodpi/wallpaper_11_small.jpg
+++ b/res/drawable-nodpi/wallpaper_11_small.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_12.jpg b/res/drawable-nodpi/wallpaper_12.jpg
index d1257ce..368ca2d 100644
--- a/res/drawable-nodpi/wallpaper_12.jpg
+++ b/res/drawable-nodpi/wallpaper_12.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_12_small.jpg b/res/drawable-nodpi/wallpaper_12_small.jpg
index 1ca48ad..8f03db6 100644
--- a/res/drawable-nodpi/wallpaper_12_small.jpg
+++ b/res/drawable-nodpi/wallpaper_12_small.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_13.jpg b/res/drawable-nodpi/wallpaper_13.jpg
index c668b6d..21147a3 100644
--- a/res/drawable-nodpi/wallpaper_13.jpg
+++ b/res/drawable-nodpi/wallpaper_13.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_13_small.jpg b/res/drawable-nodpi/wallpaper_13_small.jpg
index fedae8a..db115fb 100644
--- a/res/drawable-nodpi/wallpaper_13_small.jpg
+++ b/res/drawable-nodpi/wallpaper_13_small.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_14.jpg b/res/drawable-nodpi/wallpaper_14.jpg
index 5a8e004..54e1b79 100644
--- a/res/drawable-nodpi/wallpaper_14.jpg
+++ b/res/drawable-nodpi/wallpaper_14.jpg
Binary files differ
diff --git a/res/drawable-nodpi/wallpaper_14_small.jpg b/res/drawable-nodpi/wallpaper_14_small.jpg
index 0ac4725..f3dd837 100644
--- a/res/drawable-nodpi/wallpaper_14_small.jpg
+++ b/res/drawable-nodpi/wallpaper_14_small.jpg
Binary files differ
diff --git a/res/values-sw720dp/styles.xml b/res/values-sw720dp/styles.xml
index 77898d1..52a6abc 100644
--- a/res/values-sw720dp/styles.xml
+++ b/res/values-sw720dp/styles.xml
@@ -55,10 +55,6 @@
         <item name="android:windowActionModeOverlay">true</item>
     </style>
 
-    <style name="Theme.WallpaperPicker">
-        <item name="android:screenOrientation">unspecified</item>
-    </style>
-
     <style name="WorkspaceIcon.Portrait.AppsCustomize">
         <item name="android:shadowRadius">0.0</item> <!-- Don't use text shadow -->
         <item name="android:background">@null</item>
diff --git a/res/values-sw720dp/wallpapers.xml b/res/values-sw720dp/wallpapers.xml
deleted file mode 100644
index 1e340e4..0000000
--- a/res/values-sw720dp/wallpapers.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2009 Google Inc.
- *
- * 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.
- -->
-
-<resources>
-    <string-array name="wallpapers" translatable="false">
-    </string-array>
-</resources>
diff --git a/src/com/android/launcher3/CropView.java b/src/com/android/launcher3/CropView.java
index f904643..25ab5cd 100644
--- a/src/com/android/launcher3/CropView.java
+++ b/src/com/android/launcher3/CropView.java
@@ -17,6 +17,7 @@
 package com.android.launcher3;
 
 import android.content.Context;
+import android.graphics.Point;
 import android.graphics.RectF;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
@@ -79,17 +80,30 @@
         return new RectF(cropLeft, cropTop, cropRight, cropBottom);
     }
 
+    public Point getSourceDimensions() {
+        return new Point(mRenderer.source.getImageWidth(), mRenderer.source.getImageHeight());
+    }
+
     public void setTileSource(TileSource source, Runnable isReadyCallback) {
         super.setTileSource(source, isReadyCallback);
-        updateMinScale(getWidth(), getHeight(), source);
+        updateMinScale(getWidth(), getHeight(), source, true);
     }
 
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        updateMinScale(w, h, mRenderer.source);
+        updateMinScale(w, h, mRenderer.source, false);
     }
 
-    private void updateMinScale(int w, int h, TileSource source) {
+    public void setScale(float scale) {
         synchronized (mLock) {
+            mRenderer.scale = scale;
+        }
+    }
+
+    private void updateMinScale(int w, int h, TileSource source, boolean resetScale) {
+        synchronized (mLock) {
+            if (resetScale) {
+                mRenderer.scale = 1;
+            }
             if (source != null) {
                 mMinScale = Math.max(w / (float) source.getImageWidth(),
                         h / (float) source.getImageHeight());
diff --git a/src/com/android/launcher3/WallpaperCropActivity.java b/src/com/android/launcher3/WallpaperCropActivity.java
index 2978c85..b087506 100644
--- a/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/src/com/android/launcher3/WallpaperCropActivity.java
@@ -62,6 +62,7 @@
 
     protected class BitmapCropTask extends AsyncTask<Void, Void, Boolean> {
         Uri mInUri = null;
+        int mInResId = 0;
         InputStream mInStream;
         RectF mCropBounds = null;
         int mOutWidth, mOutHeight;
@@ -71,9 +72,10 @@
         boolean mSetWallpaper;
         boolean mSaveCroppedBitmap;
         Bitmap mCroppedBitmap;
+        Runnable mOnEndRunnable;
 
         public BitmapCropTask(Uri inUri, RectF cropBounds, int outWidth, int outHeight,
-                boolean setWallpaper, boolean saveCroppedBitmap) {
+                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
             mInUri = inUri;
             mCropBounds = cropBounds;
             mOutWidth = outWidth;
@@ -81,17 +83,35 @@
             mWPManager = WallpaperManager.getInstance(getApplicationContext());
             mSetWallpaper = setWallpaper;
             mSaveCroppedBitmap = saveCroppedBitmap;
+            mOnEndRunnable = onEndRunnable;
+        }
+
+        public BitmapCropTask(int inResId, RectF cropBounds, int outWidth, int outHeight,
+                boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
+            mInResId = inResId;
+            mCropBounds = cropBounds;
+            mOutWidth = outWidth;
+            mOutHeight = outHeight;
+            mWPManager = WallpaperManager.getInstance(getApplicationContext());
+            mSetWallpaper = setWallpaper;
+            mSaveCroppedBitmap = saveCroppedBitmap;
+            mOnEndRunnable = onEndRunnable;
         }
 
         // Helper to setup input stream
         private void regenerateInputStream() {
-            if (mInUri == null) {
-                Log.w(LOGTAG, "cannot read original file, no input URI given");
+            if (mInUri == null && mInResId == 0) {
+                Log.w(LOGTAG, "cannot read original file, no input URI or resource ID given");
             } else {
                 Utils.closeSilently(mInStream);
                 try {
-                    mInStream = new BufferedInputStream(
-                            getContentResolver().openInputStream(mInUri));
+                    if (mInUri != null) {
+                        mInStream = new BufferedInputStream(
+                                getContentResolver().openInputStream(mInUri));
+                    } else {
+                        mInStream = new BufferedInputStream(
+                                getResources().openRawResource(mInResId));
+                    }
                 } catch (FileNotFoundException e) {
                     Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e);
                 }
@@ -227,13 +247,15 @@
                             try {
                                 mWPManager.setStream(new ByteArrayInputStream(tmpOut
                                         .toByteArray()));
-                                updateWallpaperDimensions(mOutWidth, mOutHeight);
                             } catch (IOException e) {
                                 Log.w(LOGTAG, "cannot write stream to wallpaper", e);
                                 failure = true;
                             }
                         }
                     }
+                    if (mOnEndRunnable != null) {
+                        mOnEndRunnable.run();
+                    }
                 } else {
                     Log.w(LOGTAG, "cannot compress bitmap");
                     failure = true;
@@ -266,6 +288,7 @@
             editor.remove(WALLPAPER_HEIGHT_KEY);
         }
         editor.commit();
+
         WallpaperPickerActivity.suggestWallpaperDimension(getResources(),
                 sp, getWindowManager(), WallpaperManager.getInstance(this));
     }
diff --git a/src/com/android/launcher3/WallpaperPickerActivity.java b/src/com/android/launcher3/WallpaperPickerActivity.java
index a1c74d2..2d8dd76 100644
--- a/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -20,7 +20,6 @@
 import android.app.Activity;
 import android.app.WallpaperManager;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.ActivityInfo;
@@ -35,6 +34,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.util.Log;
+import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -53,7 +53,6 @@
 
 import com.android.photos.BitmapRegionTileSource;
 
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -99,14 +98,35 @@
                         meta.mGalleryImageUri, 1024, 0), null);
                 mCropView.setTouchEnabled(true);
             } else {
-                mCropView.setTileSource(new BitmapRegionTileSource(WallpaperPickerActivity.this,
-                        meta.mWallpaperResId, 1024, 0), null);
+                BitmapRegionTileSource source = new BitmapRegionTileSource(
+                        WallpaperPickerActivity.this, meta.mWallpaperResId, 1024, 0);
+                mCropView.setTileSource(source, null);
+                Point wallpaperSize = getDefaultWallpaperSize(getResources(), getWindowManager());
+                RectF crop = getMaxCropRect(source.getImageWidth(), source.getImageHeight(),
+                        wallpaperSize.x, wallpaperSize.y);
+                mCropView.setScale(wallpaperSize.x / crop.width());
                 mCropView.setTouchEnabled(false);
-                mCropView.moveToUpperLeft();
             }
         }
     };
 
+    private RectF getMaxCropRect(int inWidth, int inHeight, int outWidth, int outHeight) {
+        RectF cropRect = new RectF();
+        // Get a crop rect that will fit this
+        if (inWidth / (float) inHeight > outWidth / (float) outHeight) {
+             cropRect.top = 0;
+             cropRect.bottom = inHeight;
+             cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2;
+             cropRect.right = inWidth - cropRect.left;
+        } else {
+            cropRect.left = 0;
+            cropRect.right = inWidth;
+            cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2;
+            cropRect.bottom = inHeight - cropRect.top;
+        }
+        return cropRect;
+    }
+
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (requestCode == IMAGE_PICK && resultCode == RESULT_OK) {
             Uri uri = data.getData();
@@ -124,22 +144,11 @@
             int width = res.getDimensionPixelSize(R.dimen.wallpaperThumbnailWidth);
             int height = res.getDimensionPixelSize(R.dimen.wallpaperThumbnailHeight);
 
-            BitmapCropTask cropTask = new BitmapCropTask(uri, null, width, height, false, true);
+            BitmapCropTask cropTask =
+                    new BitmapCropTask(uri, null, width, height, false, true, null);
             Point bounds = cropTask.getImageBounds();
 
-            RectF cropRect = new RectF();
-            // Get a crop rect that will fit this
-            if (bounds.x / (float) bounds.y > width / (float) height) {
-                 cropRect.top = 0;
-                 cropRect.bottom = bounds.y;
-                 cropRect.left = (bounds.x - (width / (float) height) * bounds.y) / 2;
-                 cropRect.right = bounds.x - cropRect.left;
-            } else {
-                cropRect.left = 0;
-                cropRect.right = bounds.x;
-                cropRect.top = (bounds.y - (height / (float) width) * bounds.x) / 2;
-                cropRect.bottom = bounds.y - cropRect.top;
-            }
+            RectF cropRect = getMaxCropRect(bounds.x, bounds.y, width, height);
             cropTask.setCropBounds(cropRect);
 
             if (cropTask.cropBitmap()) {
@@ -154,6 +163,7 @@
             ThumbnailMetaData meta = new ThumbnailMetaData();
             meta.mGalleryImageUri = uri;
             pickedImageThumbnail.setTag(meta);
+            pickedImageThumbnail.setOnClickListener(mThumbnailOnClickListener);
             mThumbnailOnClickListener.onClick(pickedImageThumbnail);
         } else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY) {
             // No result code is returned; just return
@@ -209,38 +219,84 @@
                 new View.OnClickListener() {
                     @Override
                     public void onClick(View v) {
-
                         ThumbnailMetaData meta = (ThumbnailMetaData) mSelectedThumb.getTag();
                         if (meta.mLaunchesGallery) {
                             // shouldn't be selected, but do nothing
                         } else if (meta.mGalleryImageUri != null) {
                             // Get the crop
                             // TODO: get outwidth/outheight more robustly?
-                            BitmapCropTask cropTask = new BitmapCropTask(meta.mGalleryImageUri,
-                                    mCropView.getCrop(), mCropView.getWidth(), mCropView.getHeight(),
-                                    true, false);
 
+                            Point inSize = mCropView.getSourceDimensions();
+
+                            Point minDims = new Point();
+                            Point maxDims = new Point();
+                            Display d = getWindowManager().getDefaultDisplay();
+                            d.getCurrentSizeRange(minDims, maxDims);
+                            Point displaySize = new Point();
+                            d.getSize(displaySize);
+
+                            // Get the crop
+                            RectF cropRect = mCropView.getCrop();
+
+                            float cropScale = displaySize.x / (float) cropRect.width();
+                            if (displaySize.x < displaySize.y) { // PORTRAIT
+                                // Save the leftmost position for portrait mode
+                                // Extend the crop all the way to the right, for parallax
+                                float extraSpaceToRight = inSize.x - cropRect.right;
+                                cropRect.right = inSize.x;
+                                // Add some space to the left for the landscape case
+                                //float extraLandscapeWidth = maxDims.x - cropRect.width();
+                                //float shiftLeft = Math.min(
+                                //        Math.min(extraSpaceToRight, cropRect.left),
+                                //        extraLandscapeWidth / 2);
+                                //cropRect.left -= shiftLeft;
+                                // Adjust the "leftMost variable now"
+                                //float leftForPortrait = shiftLeft;
+                            } else { // LANDSCAPE
+                                float leftForPortrait = (cropRect.width() - minDims.x) / 2;
+                                cropRect.right = inSize.x;
+                                // TODO: how to actually get the proper portrait height?
+                                // This is not quite right:
+                                float portraitHeight = Math.max(maxDims.x, maxDims.y);
+                                float extraPortraitHeight = portraitHeight - cropRect.height();
+                                float expandHeight =
+                                        Math.min(Math.min(inSize.y - cropRect.bottom, cropRect.top),
+                                                extraPortraitHeight / 2);
+                                cropRect.top -= expandHeight;
+                                cropRect.bottom += expandHeight;
+                            }
+                            final int outWidth = (int) Math.ceil(cropRect.width() * cropScale);
+                            final int outHeight = (int) Math.ceil(cropRect.height() * cropScale);
+
+                            Runnable onEndCrop = new Runnable() {
+                                public void run() {
+                                    updateWallpaperDimensions(outWidth, outHeight);
+                                }
+                            };
+                            BitmapCropTask cropTask = new BitmapCropTask(meta.mGalleryImageUri,
+                                    cropRect, outWidth, outHeight, true, false, onEndCrop);
                             cropTask.execute();
                         } else if (meta.mWallpaperResId != 0) {
-                            try {
-                                WallpaperManager wm =
-                                        WallpaperManager.getInstance(getApplicationContext());
-                                wm.setResource(meta.mWallpaperResId);
-                                // passing 0 will just revert back to using the default wallpaper
-                                // size (setWallpaperDimension)
-                                updateWallpaperDimensions(0, 0);
-                                String spKey = LauncherAppState.getSharedPreferencesKey();
-                                SharedPreferences sp =
-                                        getSharedPreferences(spKey, Context.MODE_PRIVATE);
-                                SharedPreferences.Editor editor = sp.edit();
-                                editor.remove(WALLPAPER_WIDTH_KEY);
-                                editor.remove(WALLPAPER_HEIGHT_KEY);
-                                editor.commit();
-                                setResult(Activity.RESULT_OK);
-                                finish();
-                            } catch (IOException e) {
-                                Log.e(TAG, "Failed to set wallpaper: " + e);
-                            }
+                            // 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);
+                            Runnable onEndCrop = new Runnable() {
+                                public void run() {
+                                    // Passing 0, 0 will cause launcher to revert to using the
+                                    // default wallpaper size
+                                    updateWallpaperDimensions(0, 0);
+                                }
+                            };
+                            BitmapCropTask cropTask = new BitmapCropTask(meta.mWallpaperResId,
+                                    crop, outSize.x, outSize.y,
+                                    true, false, onEndCrop);
+                            cropTask.execute();
+                            setResult(Activity.RESULT_OK);
+                            finish();
                         }
                     }
                 });
@@ -371,10 +427,7 @@
         return x * aspectRatio + y;
     }
 
-    static public void suggestWallpaperDimension(Resources res,
-            final SharedPreferences sharedPrefs,
-            WindowManager windowManager,
-            final WallpaperManager wallpaperManager) {
+    static private Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) {
         Point minDims = new Point();
         Point maxDims = new Point();
         windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);
@@ -393,11 +446,20 @@
             defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
             defaultHeight = maxDim;
         }
+        return new Point(defaultWidth, defaultHeight);
+    }
+
+    static public void suggestWallpaperDimension(Resources res,
+            final SharedPreferences sharedPrefs,
+            WindowManager windowManager,
+            final WallpaperManager wallpaperManager) {
+        final Point defaultWallpaperSize = getDefaultWallpaperSize(res, windowManager);
+
         new Thread("suggestWallpaperDimension") {
             public void run() {
                 // If we have saved a wallpaper width/height, use that instead
-                int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, defaultWidth);
-                int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, defaultHeight);
+                int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, defaultWallpaperSize.x);
+                int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, defaultWallpaperSize.y);
                 wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight);
             }
         }.start();
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 05f9e2d..b6ef27d 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -971,16 +971,7 @@
     }
 
 
-    private float wallpaperOffsetForCurrentScroll() {
-        // Set wallpaper offset steps (1 / (number of screens - 1))
-        mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 1.0f);
-        if (mMaxScrollX == 0) {
-            return 0;
-        }
 
-        // do different behavior if it's  alive wallpaper?
-        return getScrollX() / (float) mMaxScrollX;
-    }
 
     protected void snapToPage(int whichPage, Runnable r) {
         if (mDelayedSnapToPageRunnable != null) {
@@ -998,7 +989,7 @@
         float mFinalOffset = 0.0f;
         float mCurrentOffset = 0.0f;
         //long mLastWallpaperOffsetUpdateTime;
-        boolean mWaitingForCallback;
+        boolean mWaitingForUpdate;
         Choreographer mChoreographer;
         Interpolator mInterpolator;
         boolean mAnimating;
@@ -1014,10 +1005,18 @@
 
         @Override
         public void doFrame(long frameTimeNanos) {
-            mWaitingForCallback = false;
-            if (computeScrollOffset()) {
-                mWallpaperManager.setWallpaperOffsets(mWindowToken,
-                        mWallpaperOffset.getCurrX(), 0f);
+            updateOffset(false);
+        }
+
+
+        private void updateOffset(boolean force) {
+            if (mWaitingForUpdate || force) {
+                mWaitingForUpdate = false;
+                if (computeScrollOffset()) {
+                    mWallpaperManager.setWallpaperOffsets(mWindowToken,
+                            mWallpaperOffset.getCurrX(), 0.5f);
+                    setWallpaperOffsetSteps();
+                }
             }
         }
 
@@ -1034,13 +1033,38 @@
                 mCurrentOffset = mFinalOffset;
             }
 
+            if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) {
+                scheduleUpdate();
+            }
             if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) {
-                scheduleCallback();
                 return true;
             }
             return false;
         }
 
+        private float wallpaperOffsetForCurrentScroll() {
+            if (getChildCount() <= 1) {
+                return 0;
+            }
+            final int lastIndex = isLayoutRtl() ? 0 : getChildCount() - 1;
+            final int firstIndex = isLayoutRtl() ? getChildCount() - 2 : 1;
+            int firstPageScrollX = getScrollForPage(firstIndex);
+            int scrollRange = getScrollForPage(lastIndex) - firstPageScrollX;
+            if (scrollRange == 0) {
+                return 0;
+            } else {
+                // do different behavior if it's  a live wallpaper?
+                float offset = (getScrollX() - firstPageScrollX) / (float) scrollRange;
+                return offset;
+            }
+        }
+
+        public void syncWithScroll() {
+            float offset = wallpaperOffsetForCurrentScroll();
+            mWallpaperOffset.setFinalX(offset);
+            updateOffset(true);
+        }
+
         public float getCurrX() {
             return mCurrentOffset;
         }
@@ -1055,8 +1079,13 @@
             mAnimationStartTime = System.currentTimeMillis();
         }
 
+        private void setWallpaperOffsetSteps() {
+            // Set wallpaper offset steps (1 / (number of screens - 1))
+            mWallpaperManager.setWallpaperOffsetSteps(1.0f / (getChildCount() - 1), 1.0f);
+        }
+
         public void setFinalX(float x) {
-            scheduleCallback();
+            scheduleUpdate();
             mFinalOffset = Math.max(0f, Math.min(x, 1.0f));
             if (getChildCount() != mNumScreens) {
                 if (mNumScreens > 0) {
@@ -1067,10 +1096,10 @@
             }
         }
 
-        private void scheduleCallback() {
-            if (!mWaitingForCallback) {
+        private void scheduleUpdate() {
+            if (!mWaitingForUpdate) {
                 mChoreographer.postFrameCallback(this);
-                mWaitingForCallback = true;
+                mWaitingForUpdate = true;
             }
         }
 
@@ -1082,11 +1111,7 @@
     @Override
     public void computeScroll() {
         super.computeScroll();
-        syncWallpaperOffsetWithScroll();
-    }
-
-    private void syncWallpaperOffsetWithScroll() {
-        mWallpaperOffset.setFinalX(wallpaperOffsetForCurrentScroll());
+        mWallpaperOffset.syncWithScroll();
     }
 
     void showOutlines() {
@@ -1325,7 +1350,7 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
-            syncWallpaperOffsetWithScroll();
+            mWallpaperOffset.syncWithScroll();
             mWallpaperOffset.jumpToFinal();
         }
         super.onLayout(changed, left, top, right, bottom);