Merge branch 'master' into honeycomb-release
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4aa5d68..247f5b7 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -75,7 +75,7 @@
             android:clearTaskOnLaunch="true"
             android:stateNotNeeded="true"
             android:theme="@style/Theme"
-            android:windowSoftInputMode="stateUnspecified|adjustPan">
+            android:windowSoftInputMode="stateUnspecified|adjustNothing">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.HOME" />
@@ -88,6 +88,7 @@
             android:name="com.android.launcher2.WallpaperChooser"
             android:label="@string/pick_wallpaper"
             android:icon="@drawable/ic_launcher_wallpaper"
+            android:theme="@style/Theme.WallpaperPicker"
             android:screenOrientation="nosensor"
             android:finishOnCloseSystemDialogs="true">
             <intent-filter>
diff --git a/res/drawable-xlarge-hdpi/textfield_end.9.png b/res/drawable-xlarge-hdpi/textfield_end.9.png
deleted file mode 100644
index be1b2b6..0000000
--- a/res/drawable-xlarge-hdpi/textfield_end.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xlarge-mdpi/textfield_end.9.png b/res/drawable-xlarge-hdpi/textfield_home_end.9.png
similarity index 92%
rename from res/drawable-xlarge-mdpi/textfield_end.9.png
rename to res/drawable-xlarge-hdpi/textfield_home_end.9.png
index 94706b4..4e79920 100644
--- a/res/drawable-xlarge-mdpi/textfield_end.9.png
+++ b/res/drawable-xlarge-hdpi/textfield_home_end.9.png
Binary files differ
diff --git a/res/drawable-xlarge-hdpi/textfield_home_start.9.png b/res/drawable-xlarge-hdpi/textfield_home_start.9.png
new file mode 100644
index 0000000..a338c69
--- /dev/null
+++ b/res/drawable-xlarge-hdpi/textfield_home_start.9.png
Binary files differ
diff --git a/res/drawable-xlarge-hdpi/textfield_start.9.png b/res/drawable-xlarge-hdpi/textfield_start.9.png
deleted file mode 100644
index 6b5ebe2..0000000
--- a/res/drawable-xlarge-hdpi/textfield_start.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xlarge-mdpi/textfield_end.9.png b/res/drawable-xlarge-mdpi/textfield_home_end.9.png
similarity index 92%
copy from res/drawable-xlarge-mdpi/textfield_end.9.png
copy to res/drawable-xlarge-mdpi/textfield_home_end.9.png
index 94706b4..753514f 100644
--- a/res/drawable-xlarge-mdpi/textfield_end.9.png
+++ b/res/drawable-xlarge-mdpi/textfield_home_end.9.png
Binary files differ
diff --git a/res/drawable-xlarge-mdpi/textfield_start.9.png b/res/drawable-xlarge-mdpi/textfield_home_start.9.png
similarity index 90%
rename from res/drawable-xlarge-mdpi/textfield_start.9.png
rename to res/drawable-xlarge-mdpi/textfield_home_start.9.png
index 8cddc34..11ee7ca 100644
--- a/res/drawable-xlarge-mdpi/textfield_start.9.png
+++ b/res/drawable-xlarge-mdpi/textfield_home_start.9.png
Binary files differ
diff --git a/res/layout-xlarge-land/launcher.xml b/res/layout-xlarge-land/launcher.xml
index 7516796..f93506e 100644
--- a/res/layout-xlarge-land/launcher.xml
+++ b/res/layout-xlarge-land/launcher.xml
@@ -63,7 +63,7 @@
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
                 android:layout_marginLeft="16dp"
-                android:background="@drawable/textfield_start"
+                android:background="@drawable/textfield_home_start"
                 android:orientation="horizontal">
                 <!-- Global search icon -->
                 <ImageView
@@ -84,7 +84,7 @@
             <LinearLayout
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
-                android:background="@drawable/textfield_end"
+                android:background="@drawable/textfield_home_end"
                 android:orientation="horizontal">
                 <!-- Voice search icon -->
                 <ImageView
diff --git a/res/layout-xlarge-port/launcher.xml b/res/layout-xlarge-port/launcher.xml
index c2ca467..088f081 100644
--- a/res/layout-xlarge-port/launcher.xml
+++ b/res/layout-xlarge-port/launcher.xml
@@ -63,7 +63,7 @@
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
                 android:layout_marginLeft="16dp"
-                android:background="@drawable/textfield_start"
+                android:background="@drawable/textfield_home_start"
                 android:orientation="horizontal">
                 <!-- Global search icon -->
                 <ImageView
@@ -84,7 +84,7 @@
             <LinearLayout
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
-                android:background="@drawable/textfield_end"
+                android:background="@drawable/textfield_home_end"
                 android:orientation="horizontal">
                 <!-- Voice search icon -->
                 <ImageView
diff --git a/res/layout-xlarge/wallpaper_chooser.xml b/res/layout-xlarge/wallpaper_chooser.xml
new file mode 100644
index 0000000..9649e88
--- /dev/null
+++ b/res/layout-xlarge/wallpaper_chooser.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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.
+*/
+-->
+
+<GridView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/gallery"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:numColumns="4"
+    android:columnWidth="@dimen/wallpaper_small_width"
+    android:stretchMode="columnWidth" />
\ No newline at end of file
diff --git a/res/layout-xlarge/wallpaper_item.xml b/res/layout-xlarge/wallpaper_item.xml
new file mode 100644
index 0000000..4246b9a
--- /dev/null
+++ b/res/layout-xlarge/wallpaper_item.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/wallpaper_small_width"
+    android:layout_height="wrap_content"
+    android:padding="5dp"
+    android:scaleType="fitXY"
+    android:adjustViewBounds="false"
+    android:focusable="false" />
diff --git a/res/layout/wallpaper_chooser_base.xml b/res/layout/wallpaper_chooser_base.xml
new file mode 100644
index 0000000..8447027
--- /dev/null
+++ b/res/layout/wallpaper_chooser_base.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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.
+*/
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/wallpaper_chooser_base"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+</FrameLayout>
diff --git a/res/values-xlarge/config.xml b/res/values-xlarge/config.xml
index fa7af5e..3654753 100644
--- a/res/values-xlarge/config.xml
+++ b/res/values-xlarge/config.xml
@@ -37,7 +37,7 @@
     <!-- When dragging items on the workspace, the number of pixels by which the position of
          the drag view should be offset from the position of the original view. -->
     <integer name="config_dragViewOffsetX">0</integer>
-    <integer name="config_dragViewOffsetY">12</integer>
+    <integer name="config_dragViewOffsetY">-12</integer>
 
     <!-- When items are dropped on the mini screens in customize mode, we have a bounce animation
          of the bright green hover outline, and then fade out the outline at the end. These are
diff --git a/res/values-xlarge/dimens.xml b/res/values-xlarge/dimens.xml
index d98674f..7033553 100644
--- a/res/values-xlarge/dimens.xml
+++ b/res/values-xlarge/dimens.xml
@@ -50,4 +50,7 @@
     <dimen name="delete_zone_size">50dip</dimen>
     <dimen name="delete_zone_min_padding">10dip</dimen>
     <dimen name="delete_zone_max_padding">20dip</dimen>
+
+    <!-- dimensions for the wallpaper picker wallpaper thumbnail width -->
+    <dimen name="wallpaper_small_width">170dp</dimen>
 </resources>
diff --git a/res/values-xlarge/strings.xml b/res/values-xlarge/strings.xml
new file mode 100644
index 0000000..d8bdbbc
--- /dev/null
+++ b/res/values-xlarge/strings.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2010 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Title of the wallpaper selection dialog [CHAR_LIMIT=40]-->
+    <string name="wallpaper_dialog_title">Select wallpaper</string>
+
+    <!-- Negative button text for the wallpaper selection dialog [CHAR_LIMIT=40]-->
+    <string name="wallpaper_cancel">Cancel</string>
+</resources>
\ No newline at end of file
diff --git a/res/values-xlarge/styles.xml b/res/values-xlarge/styles.xml
index fcbd969..5198d30 100644
--- a/res/values-xlarge/styles.xml
+++ b/res/values-xlarge/styles.xml
@@ -18,6 +18,9 @@
 -->
 
 <resources>
+    <style name="Theme.WallpaperPicker" parent="android:Theme.Holo.Dialog.NoFrame">
+    </style>
+
     <style name="Theme" parent="android:Theme.Holo">
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowActionModeOverlay">true</item>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d6cd3ee..39739c2 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -44,4 +44,8 @@
     <!-- the area at the edge of the screen that makes the workspace go left
          or right while you're dragging. -->
     <dimen name="scroll_zone">20dp</dimen>
+
+    <!-- How much the content view of an alert dialog should be inset (currently used
+        for the WallpaperChooser in XLarge mode) -->
+    <dimen name="alert_dialog_content_inset">15dp</dimen>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index a97b3c5..c867d38 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -18,6 +18,10 @@
 -->
 
 <resources>
+    <style name="Theme.WallpaperPicker" parent="android:Theme">
+        <item name="android:windowNoTitle">true</item>
+    </style>
+
     <style name="Theme.Base" parent="android:Theme.Wallpaper">
     </style>
 
diff --git a/src/com/android/launcher2/AllApps2D.java b/src/com/android/launcher2/AllApps2D.java
index 9764f23..66d9395 100644
--- a/src/com/android/launcher2/AllApps2D.java
+++ b/src/com/android/launcher2/AllApps2D.java
@@ -201,10 +201,16 @@
         }
     }
 
+    @Override
     public void setDragController(DragController dragger) {
         mDragController = dragger;
     }
 
+    @Override
+    public void onDragViewVisible() {
+    }
+
+    @Override
     public void onDropCompleted(View target, boolean success) {
     }
 
diff --git a/src/com/android/launcher2/AllApps3D.java b/src/com/android/launcher2/AllApps3D.java
index b56a9c9..b75d08a 100644
--- a/src/com/android/launcher2/AllApps3D.java
+++ b/src/com/android/launcher2/AllApps3D.java
@@ -192,6 +192,7 @@
             destroyRenderScript();
             sRS = null;
             sRollo = null;
+            super.onDetachedFromWindow();
         }
     }
 
@@ -735,10 +736,16 @@
         return false;
     }
 
+    @Override
     public void setDragController(DragController dragger) {
         mDragController = dragger;
     }
 
+    @Override
+    public void onDragViewVisible() {
+    }
+
+    @Override
     public void onDropCompleted(View target, boolean success) {
     }
 
diff --git a/src/com/android/launcher2/AllAppsPagedView.java b/src/com/android/launcher2/AllAppsPagedView.java
index 1f3df5f..4ca5b47 100644
--- a/src/com/android/launcher2/AllAppsPagedView.java
+++ b/src/com/android/launcher2/AllAppsPagedView.java
@@ -237,6 +237,10 @@
     }
 
     @Override
+    public void onDragViewVisible() {
+    }
+
+    @Override
     public void onDropCompleted(View target, boolean success) {
         // close the choice action mode if we have a proper drop
         if (target != this) {
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index e340101..023e9f4 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -1241,7 +1241,6 @@
     void onDragChild(View child) {
         LayoutParams lp = (LayoutParams) child.getLayoutParams();
         lp.isDragging = true;
-        child.setVisibility(View.GONE);
     }
 
     /**
diff --git a/src/com/android/launcher2/CustomizePagedView.java b/src/com/android/launcher2/CustomizePagedView.java
index 6db6aa9..b48d4ab 100644
--- a/src/com/android/launcher2/CustomizePagedView.java
+++ b/src/com/android/launcher2/CustomizePagedView.java
@@ -312,6 +312,10 @@
     }
 
     @Override
+    public void onDragViewVisible() {
+    }
+
+    @Override
     public void onClick(View v) {
         // Return early if this is not initiated from a touch
         if (!v.isInTouchMode()) return;
@@ -490,16 +494,14 @@
 
             mLauncher.getWorkspace().onDragStartedWithItemMinSize(
                     createWidgetInfo.minWidth, createWidgetInfo.minHeight);
-            mDragController.startDrag(v, b, this, createWidgetInfo, DragController.DRAG_ACTION_COPY,
-                    null);
+            mDragController.startDrag(v, b, this, createWidgetInfo, DragController.DRAG_ACTION_COPY, null);
 
             // Cleanup the icon
             b.recycle();
             return true;
         case ShortcutCustomization:
             createItemInfo = (PendingAddItemInfo) v.getTag();
-            mDragController.startDrag(
-                    v, this, createItemInfo, DragController.DRAG_ACTION_COPY, null);
+            mDragController.startDrag(v, this, createItemInfo, DragController.DRAG_ACTION_COPY);
             mLauncher.getWorkspace().onDragStartedWithItemSpans(1, 1);
             return true;
         case ApplicationCustomization:
diff --git a/src/com/android/launcher2/DragController.java b/src/com/android/launcher2/DragController.java
index 2b566b7..a0a44a5 100644
--- a/src/com/android/launcher2/DragController.java
+++ b/src/com/android/launcher2/DragController.java
@@ -320,6 +320,13 @@
         DragView dragView = mDragView = new DragView(mContext, b, registrationX, registrationY,
                 textureLeft, textureTop, textureWidth, textureHeight);
 
+        final DragSource dragSource = source;
+        dragView.setOnDrawRunnable(new Runnable() {
+            public void run() {
+                dragSource.onDragViewVisible();
+            };
+        });
+
         if (dragRegion != null) {
             dragView.setDragRegion(dragRegionLeft, dragRegion.top,
                     dragRegion.right - dragRegionLeft, dragRegion.bottom - dragRegionTop);
diff --git a/src/com/android/launcher2/DragSource.java b/src/com/android/launcher2/DragSource.java
index 7c6ca58..11cdcc9 100644
--- a/src/com/android/launcher2/DragSource.java
+++ b/src/com/android/launcher2/DragSource.java
@@ -24,5 +24,12 @@
  */
 public interface DragSource {
     void setDragController(DragController dragger);
+
+    /**
+     * Callback from the DragController when it begins drawing the drag view.
+     * This allows the DragSource to dim or hide the original view.
+     */
+    void onDragViewVisible();
+
     void onDropCompleted(View target, boolean success);
 }
diff --git a/src/com/android/launcher2/DragView.java b/src/com/android/launcher2/DragView.java
index ca0e7b4..947184f 100644
--- a/src/com/android/launcher2/DragView.java
+++ b/src/com/android/launcher2/DragView.java
@@ -19,6 +19,8 @@
 
 import com.android.launcher.R;
 
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -32,8 +34,9 @@
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowManagerImpl;
+import android.view.animation.DecelerateInterpolator;
 
-public class DragView extends View implements TweenCallback {
+public class DragView extends View {
     private Bitmap mBitmap;
     private Paint mPaint;
     private int mRegistrationX;
@@ -44,14 +47,22 @@
     private int mDragRegionWidth;
     private int mDragRegionHeight;
 
-    SymmetricalLinearTween mTween;
-    private float mScale;
-    private float mAnimationScale = 1.0f;
+    ValueAnimator mAnim;
+    private float mScale = 1.0f;
+    private float mOffsetX = 0.0f;
+    private float mOffsetY = 0.0f;
 
     private WindowManager.LayoutParams mLayoutParams;
     private WindowManager mWindowManager;
 
     /**
+     * A callback to be called the first time this view is drawn.
+     * This allows the originator of the drag to dim or hide the original view as soon
+     * as the DragView is drawn.
+     */
+    private Runnable mOnDrawRunnable = null;
+
+    /**
      * Construct the drag view.
      * <p>
      * The registration point is the point inside our view that the touch events should
@@ -70,20 +81,48 @@
         final int dragScale = res.getInteger(R.integer.config_dragViewExtraPixels);
 
         mWindowManager = WindowManagerImpl.getDefault();
-        
-        mTween = new SymmetricalLinearTween(false, 110 /*ms duration*/, this);
 
         Matrix scale = new Matrix();
-        float scaleFactor = width;
-        scaleFactor = mScale = (scaleFactor + dragScale) / scaleFactor;
-        scale.setScale(scaleFactor, scaleFactor);
+        final float scaleFactor = (width + dragScale) / width;
+        if (scaleFactor != 1.0f) {
+            scale.setScale(scaleFactor, scaleFactor);
+        }
+
+        final int offsetX = res.getInteger(R.integer.config_dragViewOffsetX);
+        final int offsetY = res.getInteger(R.integer.config_dragViewOffsetY);
+
+        // Animate the view into the correct position
+        mAnim = ValueAnimator.ofFloat(0.0f, 1.0f);
+        mAnim.setDuration(110);
+        mAnim.setInterpolator(new DecelerateInterpolator(2.5f));
+        mAnim.addUpdateListener(new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                final float value = (Float) animation.getAnimatedValue();
+
+                final int deltaX = (int) ((value * offsetX) - mOffsetX);
+                final int deltaY = (int) ((value * offsetY) - mOffsetY);
+
+                mOffsetX += deltaX;
+                mOffsetY += deltaY;
+
+                if (getParent() == null) {
+                    animation.cancel();
+                } else {
+                    WindowManager.LayoutParams lp = mLayoutParams;
+                    lp.x += deltaX;
+                    lp.y += deltaY;
+                    mWindowManager.updateViewLayout(DragView.this, lp);
+                }
+            }
+        });
 
         mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height, scale, true);
         setDragRegion(0, 0, width, height);
 
         // The point in our scaled bitmap that the touch events are located
-        mRegistrationX = registrationX + res.getInteger(R.integer.config_dragViewOffsetX);
-        mRegistrationY = registrationY + res.getInteger(R.integer.config_dragViewOffsetY);
+        mRegistrationX = registrationX;
+        mRegistrationY = registrationY;
     }
 
     public void setDragRegion(int left, int top, int width, int height) {
@@ -93,6 +132,10 @@
         mDragRegionHeight = height;
     }
 
+    public void setOnDrawRunnable(Runnable r) {
+        mOnDrawRunnable = r;
+    }
+
     public int getScaledDragRegionXOffset() {
         return -(int)((mScale - 1.0f) * mDragRegionWidth / 2);
     }
@@ -139,13 +182,15 @@
             p.setColor(0xaaffffff);
             canvas.drawRect(0, 0, getWidth(), getHeight(), p);
         }
-        float scale = mAnimationScale;
-        if (scale < 0.999f) { // allow for some float error
-            float width = mBitmap.getWidth();
-            float offset = (width-(width*scale))/2;
-            canvas.translate(offset, offset);
-            canvas.scale(scale, scale);
+
+        // Call the callback if we haven't already been detached
+        if (getParent() != null) {
+            if (mOnDrawRunnable != null) {
+                mOnDrawRunnable.run();
+                mOnDrawRunnable = null;
+            }
         }
+
         canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
     }
 
@@ -155,17 +200,6 @@
         mBitmap.recycle();
     }
 
-    public void onTweenValueChanged(float value, float oldValue) {
-        mAnimationScale = (1.0f+((mScale-1.0f)*value))/mScale;
-        invalidate();
-    }
-
-    public void onTweenStarted() {
-    }
-
-    public void onTweenFinished() {
-    }
-
     public void setPaint(Paint paint) {
         mPaint = paint;
         invalidate();
@@ -187,7 +221,7 @@
         lp = new WindowManager.LayoutParams(
                 ViewGroup.LayoutParams.WRAP_CONTENT,
                 ViewGroup.LayoutParams.WRAP_CONTENT,
-                touchX-mRegistrationX, touchY-mRegistrationY,
+                touchX - mRegistrationX, touchY - mRegistrationY,
                 WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                     | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
@@ -201,8 +235,7 @@
 
         mWindowManager.addView(this, lp);
 
-        mAnimationScale = 1.0f/mScale;
-        mTween.start(true);
+        mAnim.start();
     }
     
     /**
@@ -213,8 +246,8 @@
      */
     void move(int touchX, int touchY) {
         WindowManager.LayoutParams lp = mLayoutParams;
-        lp.x = touchX - mRegistrationX;
-        lp.y = touchY - mRegistrationY;
+        lp.x = touchX - mRegistrationX + (int) mOffsetX;
+        lp.y = touchY - mRegistrationY + (int) mOffsetY;
         mWindowManager.updateViewLayout(this, lp);
     }
 
diff --git a/src/com/android/launcher2/Folder.java b/src/com/android/launcher2/Folder.java
index 018b284..cb450b9 100644
--- a/src/com/android/launcher2/Folder.java
+++ b/src/com/android/launcher2/Folder.java
@@ -108,13 +108,19 @@
         return true;
     }
 
+    @Override
     public void setDragController(DragController dragController) {
         mDragController = dragController;
     }
 
+    @Override
     public void onDropCompleted(View target, boolean success) {
     }
 
+    @Override
+    public void onDragViewVisible() {
+    }
+
     /**
      * Sets the adapter used to populate the content area. The adapter must only
      * contains ShortcutInfo items.
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index 55a6176..2a2d364 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -1101,7 +1101,7 @@
         // supporting spring-loaded mini-screens; however, leaving the ability to directly place
         // a widget on the home screen in case we want to add it in the future
         int[] touchXY = null;
-        if (mAddDropPosition[0] > -1 && mAddDropPosition[1] > -1) {
+        if (mAddDropPosition != null && mAddDropPosition[0] > -1 && mAddDropPosition[1] > -1) {
             touchXY = mAddDropPosition;
         }
         boolean findNearestVacantAreaFailed = false;
@@ -1114,15 +1114,12 @@
             findNearestVacantAreaFailed = (result == null);
             foundCellSpan = !findNearestVacantAreaFailed;
         } else {
-            if (mAddIntersectCellX != -1 && mAddIntersectCellY != -1) {
-                // if we long pressed on an empty cell to bring up a menu,
-                // make sure we intersect the empty cell
-                foundCellSpan = layout.findCellForSpanThatIntersects(cellXY, spanXY[0], spanXY[1],
-                        mAddIntersectCellX, mAddIntersectCellY);
-            } else {
-                // if we went through the menu -> add, just find any spot
-                foundCellSpan = layout.findCellForSpan(cellXY, spanXY[0], spanXY[1]);
-            }
+            // if we long pressed on an empty cell to bring up a menu,
+            // make sure we intersect the empty cell
+            // if mAddIntersectCellX/Y are -1 (e.g. we used menu -> add) then
+            // findCellForSpanThatIntersects will just ignore them
+            foundCellSpan = layout.findCellForSpanThatIntersects(cellXY, spanXY[0], spanXY[1],
+                    mAddIntersectCellX, mAddIntersectCellY);
         }
 
         if (!foundCellSpan) {
@@ -1923,6 +1920,8 @@
      */
     public void onClickSearchButton(View v) {
         startSearch(null, false, null, true);
+        // Use a custom animation for launching search
+        overridePendingTransition(R.anim.fade_in_fast, R.anim.fade_out_fast);
     }
 
     /**
@@ -2776,7 +2775,9 @@
     }
 
     void showWorkspace(boolean animated, CellLayout layout) {
-        if (layout != null && animated) {
+        if (layout != null) {
+            // always animated, but that's ok since we never specify a layout and
+            // want no animation
             mWorkspace.unshrink(layout);
         } else {
             mWorkspace.unshrink(animated);
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index 264e839..d24eef4 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -36,6 +36,7 @@
 import android.view.ViewParent;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.view.animation.Animation.AnimationListener;
 import android.widget.Checkable;
 import android.widget.Scroller;
@@ -55,10 +56,12 @@
     // The min drag distance to trigger a page shift (regardless of velocity)
     private static final int MIN_LENGTH_FOR_MOVE = 200;
 
-    private static final int PAGE_SNAP_ANIMATION_DURATION = 750;
+    private static final int PAGE_SNAP_ANIMATION_DURATION = 550;
     protected static final float NANOTIME_DIV = 1000000000.0f;
 
-    private static final float OVERSCROLL_DAMP_FACTOR = 0.22f;
+    private static final float OVERSCROLL_DAMP_FACTOR = 0.08f;
+    private static final int MINIMUM_SNAP_VELOCITY = 2200;
+    private static final int MIN_FLING_VELOCITY = 250;
 
     // the velocity at which a fling gesture will cause us to snap to the next page
     protected int mSnapVelocity = 500;
@@ -209,7 +212,7 @@
         mDirtyPageContent = new ArrayList<Boolean>();
         mDirtyPageContent.ensureCapacity(32);
         mPageViewIconCache = new PagedViewIconCache();
-        mScroller = new Scroller(getContext());
+        mScroller = new Scroller(getContext(), new ScrollInterpolator());
         mCurrentPage = 0;
         mCenterPagesVertically = true;
 
@@ -353,7 +356,11 @@
             mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
             mNextPage = INVALID_PAGE;
             notifyPageSwitchListener();
-            pageEndMoving();
+            // We don't want to trigger a page end moving unless the page has settled
+            // and the user has stopped scrolling
+            if (mTouchState == TOUCH_STATE_REST) {
+                pageEndMoving();
+            }
             return true;
         }
         return false;
@@ -827,8 +834,22 @@
         return false;
     }
 
+    // This curve determines how the effect of scrolling over the limits of the page dimishes
+    // as the user pulls further and further from the bounds
+    private float overScrollInfluenceCurve(float f) {
+        f -= 1.0f;
+        return f * f * f + 1.0f;
+    }
+
     protected void overScroll(float amount) {
-        int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * amount);
+        int screenSize = getMeasuredWidth();
+
+        float f = (amount / screenSize);
+
+        if (f == 0) return;
+        f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f)));
+
+        int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
         if (amount < 0) {
             mScrollX = overScrollAmount;
         } else {
@@ -902,8 +923,7 @@
 
                 final int snapVelocity = mSnapVelocity;
                 if ((isSignificantMove && deltaX > 0 ||
-                        (isfling && velocityX > snapVelocity)) &&
-                        mCurrentPage > 0) {
+                        (isfling && velocityX > snapVelocity)) && mCurrentPage > 0) {
                     snapToPageWithVelocity(mCurrentPage - 1, velocityX);
                 } else if ((isSignificantMove && deltaX < 0 ||
                         (isfling && velocityX < -snapVelocity)) &&
@@ -1048,10 +1068,58 @@
         snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
     }
 
+    private static class ScrollInterpolator implements Interpolator {
+        public ScrollInterpolator() {
+        }
+
+        public float getInterpolation(float t) {
+            t -= 1.0f;
+            return t*t*t*t*t + 1;
+        }
+    }
+
+    // We want the duration of the page snap animation to be influenced by the distance that
+    // the screen has to travel, however, we don't want this duration to be effected in a
+    // purely linear fashion. Instead, we use this method to moderate the effect that the distance
+    // of travel has on the overall snap duration.
+    float distanceInfluenceForSnapDuration(float f) {
+        f -= 0.5f; // center the values about 0.
+        f *= 0.3f * Math.PI / 2.0f;
+        return (float) Math.sin(f);
+    }
+
     protected void snapToPageWithVelocity(int whichPage, int velocity) {
-        // We ignore velocity in this implementation, but children (e.g. SmoothPagedView)
-        // can use it
-        snapToPage(whichPage);
+        whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
+        int halfScreenSize = getMeasuredWidth() / 2;
+
+        final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
+        int delta = newX - mUnboundedScrollX;
+        int duration = 0;
+
+        if (Math.abs(velocity) < MIN_FLING_VELOCITY) {
+            // If the velocity is low enough, then treat this more as an automatic page advance
+            // as opposed to an apparent physical response to flinging
+            snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+            return;
+        }
+
+        // Here we compute a "distance" that will be used in the computation of the overall
+        // snap duration. This is a function of the actual distance that needs to be traveled;
+        // we keep this value close to half screen size in order to reduce the variance in snap
+        // duration as a function of the distance the page needs to travel.
+        float distanceRatio = 1.0f * Math.abs(delta) / 2 * halfScreenSize;
+        float distance = halfScreenSize + halfScreenSize *
+                distanceInfluenceForSnapDuration(distanceRatio);
+
+        velocity = Math.abs(velocity);
+        velocity = Math.max(MINIMUM_SNAP_VELOCITY, velocity);
+
+        // we want the page's snap velocity to approximately match the velocity at which the
+        // user flings, so we scale the duration by a value near to the derivative of the scroll
+        // interpolator at zero, ie. 5. We use 6 to make it a little slower.
+        duration = 6 * Math.round(1000 * Math.abs(distance / velocity));
+
+        snapToPage(whichPage, delta, duration);
     }
 
     protected void snapToPage(int whichPage) {
diff --git a/src/com/android/launcher2/SmoothPagedView.java b/src/com/android/launcher2/SmoothPagedView.java
index ee8bca2..8b0a835 100644
--- a/src/com/android/launcher2/SmoothPagedView.java
+++ b/src/com/android/launcher2/SmoothPagedView.java
@@ -21,7 +21,6 @@
 import android.view.animation.Interpolator;
 import android.widget.Scroller;
 
-
 public abstract class SmoothPagedView extends PagedView {
     private static final float SMOOTHING_SPEED = 0.75f;
     private static final float SMOOTHING_CONSTANT = (float) (0.016 / Math.log(SMOOTHING_SPEED));
@@ -29,8 +28,8 @@
     private float mBaseLineFlingVelocity;
     private float mFlingVelocityInfluence;
 
-    static final int OVERSHOOT_MODE = 0;
-    static final int QUINTIC_MODE = 1;
+    static final int DEFAULT_MODE = 0;
+    static final int X_LARGE_MODE = 1;
 
     int mScrollMode;
 
@@ -60,16 +59,6 @@
         }
     }
 
-    private static class QuinticInterpolator implements Interpolator {
-        public QuinticInterpolator() {
-        }
-
-        public float getInterpolation(float t) {
-            t -= 1.0f;
-            return t*t*t*t*t + 1;
-        }
-    }
-
     /**
      * Used to inflate the Workspace from XML.
      *
@@ -93,12 +82,12 @@
         mUsePagingTouchSlop = false;
 
         // This means that we'll take care of updating the scroll parameter ourselves (we do it
-        // in computeScroll)
-        mDeferScrollUpdate = true;
+        // in computeScroll), we only do this in the OVERSHOOT_MODE, ie. on phones
+        mDeferScrollUpdate = mScrollMode != X_LARGE_MODE;
     }
 
     protected int getScrollMode() {
-        return OVERSHOOT_MODE;
+        return DEFAULT_MODE;
     }
 
     /**
@@ -109,26 +98,30 @@
         super.init();
 
         mScrollMode = getScrollMode();
-        if (mScrollMode == QUINTIC_MODE) {
-            mBaseLineFlingVelocity = 700.0f;
-            mFlingVelocityInfluence = 0.8f;
-            mScrollInterpolator = new QuinticInterpolator();
-        } else {  // QUINTIC_MODE
+        if (mScrollMode == DEFAULT_MODE) {
             mBaseLineFlingVelocity = 2500.0f;
             mFlingVelocityInfluence = 0.4f;
             mScrollInterpolator = new WorkspaceOvershootInterpolator();
+            mScroller = new Scroller(getContext(), mScrollInterpolator);
         }
-        mScroller = new Scroller(getContext(), mScrollInterpolator);
     }
 
     @Override
     protected void snapToDestination() {
-        snapToPageWithVelocity(getPageNearestToCenterOfScreen(), 0);
+        if (mScrollMode == X_LARGE_MODE) {
+            super.snapToDestination();
+        } else {
+            snapToPageWithVelocity(getPageNearestToCenterOfScreen(), 0);
+        }
     }
 
     @Override
     protected void snapToPageWithVelocity(int whichPage, int velocity) {
-        snapToPageWithVelocity(whichPage, 0, true);
+        if (mScrollMode == X_LARGE_MODE) {
+            super.snapToPageWithVelocity(whichPage, velocity);
+        } else {
+            snapToPageWithVelocity(whichPage, 0, true);
+        }
     }
 
     private void snapToPageWithVelocity(int whichPage, int velocity, boolean settle) {
@@ -139,23 +132,16 @@
         final int screenDelta = Math.max(1, Math.abs(whichPage - mCurrentPage));
         final int newX = getChildOffset(whichPage) - getRelativeChildOffset(whichPage);
         final int delta = newX - mUnboundedScrollX;
-        int duration;
-        if (mScrollMode == OVERSHOOT_MODE) {
-            duration = (screenDelta + 1) * 100;
-        } else { // QUINTIC_MODE
-            duration = Math.round(Math.abs(delta) * 0.6f);
-        }
+        int duration = (screenDelta + 1) * 100;
 
         if (!mScroller.isFinished()) {
             mScroller.abortAnimation();
         }
 
-        if (mScrollMode == OVERSHOOT_MODE) {
-            if (settle) {
-                ((WorkspaceOvershootInterpolator) mScrollInterpolator).setDistance(screenDelta);
-            } else {
-                ((WorkspaceOvershootInterpolator) mScrollInterpolator).disableSettle();
-            }
+        if (settle) {
+            ((WorkspaceOvershootInterpolator) mScrollInterpolator).setDistance(screenDelta);
+        } else {
+            ((WorkspaceOvershootInterpolator) mScrollInterpolator).disableSettle();
         }
 
         velocity = Math.abs(velocity);
@@ -170,26 +156,33 @@
 
     @Override
     protected void snapToPage(int whichPage) {
-        snapToPageWithVelocity(whichPage, 0, false);
+       if (mScrollMode == X_LARGE_MODE) {
+           super.snapToPage(whichPage);
+       } else {
+           snapToPageWithVelocity(whichPage, 0, false);
+       }
     }
 
     @Override
     public void computeScroll() {
-        boolean scrollComputed = computeScrollHelper();
+        if (mScrollMode == X_LARGE_MODE) {
+            super.computeScroll();
+        } else {
+            boolean scrollComputed = computeScrollHelper();
 
-        if (!scrollComputed && mTouchState == TOUCH_STATE_SCROLLING) {
-            final float now = System.nanoTime() / NANOTIME_DIV;
-            final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT);
+            if (!scrollComputed && mTouchState == TOUCH_STATE_SCROLLING) {
+                final float now = System.nanoTime() / NANOTIME_DIV;
+                final float e = (float) Math.exp((now - mSmoothingTime) / SMOOTHING_CONSTANT);
 
-            final float dx = mTouchX - mUnboundedScrollX;
-            scrollTo(Math.round(mUnboundedScrollX + dx * e), mScrollY);
-            mSmoothingTime = now;
+                final float dx = mTouchX - mUnboundedScrollX;
+                scrollTo(Math.round(mUnboundedScrollX + dx * e), mScrollY);
+                mSmoothingTime = now;
 
-            // Keep generating points as long as we're more than 1px away from the target
-            if (dx > 1.f || dx < -1.f) {
-                invalidate();
+                // Keep generating points as long as we're more than 1px away from the target
+                if (dx > 1.f || dx < -1.f) {
+                    invalidate();
+                }
             }
         }
-
     }
 }
diff --git a/src/com/android/launcher2/WallpaperChooser.java b/src/com/android/launcher2/WallpaperChooser.java
index bf8ba2e..0a8d9f4 100644
--- a/src/com/android/launcher2/WallpaperChooser.java
+++ b/src/com/android/launcher2/WallpaperChooser.java
@@ -17,7 +17,13 @@
 package com.android.launcher2;
 
 import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentTransaction;
 import android.app.WallpaperManager;
+import android.content.Context;
+import android.content.DialogInterface;
 import android.content.res.Resources;
 import android.graphics.BitmapFactory;
 import android.graphics.Bitmap;
@@ -28,27 +34,26 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.Window;
 import android.view.View.OnClickListener;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
 import android.widget.Gallery;
+import android.widget.GridView;
 import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.SpinnerAdapter;
 
 import java.io.IOException;
 import java.util.ArrayList;
 
 import com.android.launcher.R;
 
-public class WallpaperChooser extends Activity implements AdapterView.OnItemSelectedListener,
-        OnClickListener {
+public class WallpaperChooser extends Activity {
     private static final String TAG = "Launcher.WallpaperChooser";
 
-    private Gallery mGallery;
-    private ImageView mImageView;
-    private boolean mIsWallpaperSet;
-
-    private Bitmap mBitmap;
+    private ViewGroup mWallpaperChooserBase;
+    private ImageView mImageView = null;
+    private Bitmap mBitmap = null;
 
     private ArrayList<Integer> mThumbs;
     private ArrayList<Integer> mImages;
@@ -57,20 +62,35 @@
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
-        requestWindowFeature(Window.FEATURE_NO_TITLE);
+        /* We need some container to attach to in order for the fragment to be
+         * considered embedded, so inflate an empty FrameLayout and use that
+         * as the parent view
+         */
+        setContentView(R.layout.wallpaper_chooser_base);
+        mWallpaperChooserBase = (ViewGroup) findViewById(R.id.wallpaper_chooser_base);
 
         findWallpapers();
 
-        setContentView(R.layout.wallpaper_chooser);
+        DialogFragment newFragment = new WallpaperDialogFragment(this);
+        if (LauncherApplication.isScreenXLarge()) {
+            // Display a dialog instead of embedding the view in the activity
+            newFragment.show(getFragmentManager(), "dialog");
+        } else {
+            // Embed the fragment in the base view
+            FragmentTransaction ft = getFragmentManager().openTransaction();
+            ft.add(R.id.wallpaper_chooser_base, newFragment);
+            ft.commit();
+        }
+    }
 
-        mGallery = (Gallery) findViewById(R.id.gallery);
-        mGallery.setAdapter(new ImageAdapter(this));
-        mGallery.setOnItemSelectedListener(this);
-        mGallery.setCallbackDuringFling(false);
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
 
-        findViewById(R.id.set).setOnClickListener(this);
-
-        mImageView = (ImageView) findViewById(R.id.wallpaper);
+        if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
+            mLoader.cancel(true);
+            mLoader = null;
+        }
     }
 
     private void findWallpapers() {
@@ -105,40 +125,7 @@
         }
     }
 
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mIsWallpaperSet = false;
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        
-        if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
-            mLoader.cancel(true);
-            mLoader = null;
-        }
-    }
-
-    public void onItemSelected(AdapterView parent, View v, int position, long id) {
-        if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
-            mLoader.cancel();
-        }
-        mLoader = (WallpaperLoader) new WallpaperLoader().execute(position);
-    }
-
-    /*
-     * When using touch if you tap an image it triggers both the onItemClick and
-     * the onTouchEvent causing the wallpaper to be set twice. Ensure we only
-     * set the wallpaper once.
-     */
     private void selectWallpaper(int position) {
-        if (mIsWallpaperSet) {
-            return;
-        }
-
-        mIsWallpaperSet = true;
         try {
             WallpaperManager wpm = (WallpaperManager)getSystemService(WALLPAPER_SERVICE);
             wpm.setResource(mImages.get(position));
@@ -149,10 +136,95 @@
         }
     }
 
-    public void onNothingSelected(AdapterView parent) {
+    private class WallpaperDialogFragment extends DialogFragment implements
+            AdapterView.OnItemSelectedListener, AdapterView.OnItemClickListener {
+        private Context mContext;
+
+        public WallpaperDialogFragment(Context context) {
+            super();
+            mContext = context;
+            setCancelable(true);
+        }
+
+        @Override
+        public void onDismiss(DialogInterface dialog) {
+            WallpaperChooser.this.finish();
+        }
+
+        /* This will only be called when in XLarge mode, since this Fragment is invoked like
+         * a dialog in that mode
+         */
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final View v = getLayoutInflater().inflate(
+                    R.layout.wallpaper_chooser, mWallpaperChooserBase, false);
+
+            GridView gridView = (GridView) v.findViewById(R.id.gallery);
+            gridView.setOnItemClickListener(this);
+            gridView.setAdapter(new ImageAdapter(WallpaperChooser.this));
+
+            final int viewInset =
+                    getResources().getDimensionPixelSize(R.dimen.alert_dialog_content_inset);
+
+            AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+            builder.setCancelable(true);
+            builder.setNegativeButton(R.string.wallpaper_cancel, null);
+            builder.setTitle(R.string.wallpaper_dialog_title);
+            builder.setView(gridView, viewInset, viewInset, viewInset, viewInset);
+            return builder.create();
+        }
+
+        /* This will be called on both XLarge and small screens, but since the dialog
+         * is already created on XLarge, we want to skip this view creation
+         */
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            /* Only generate a custom view if this fragment is being embedded in a view,
+             * i.e: on a small screen
+             */
+            if (!LauncherApplication.isScreenXLarge()) {
+                View view = inflater.inflate(R.layout.wallpaper_chooser, container, false);
+
+                final Gallery gallery = (Gallery) view.findViewById(R.id.gallery);
+                gallery.setCallbackDuringFling(false);
+                gallery.setOnItemSelectedListener(this);
+                gallery.setAdapter(new ImageAdapter(WallpaperChooser.this));
+
+                View setButton = view.findViewById(R.id.set);
+                setButton.setOnClickListener(new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        selectWallpaper(gallery.getSelectedItemPosition());
+                    }
+                });
+                mImageView = (ImageView) view.findViewById(R.id.wallpaper);
+                return view;
+            }
+            return super.onCreateView(inflater, container, savedInstanceState);
+        }
+
+        // Click handler for the Dialog's GridView
+        @Override
+        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+            selectWallpaper(position);
+        }
+
+        // Selection handler for the embedded Gallery view
+        @Override
+        public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+            if (mLoader != null && mLoader.getStatus() != WallpaperLoader.Status.FINISHED) {
+                mLoader.cancel();
+            }
+            mLoader = (WallpaperLoader) new WallpaperLoader().execute(position);
+        }
+
+        @Override
+        public void onNothingSelected(AdapterView<?> parent) {
+        }
     }
 
-    private class ImageAdapter extends BaseAdapter {
+    private class ImageAdapter extends BaseAdapter implements ListAdapter, SpinnerAdapter {
         private LayoutInflater mLayoutInflater;
 
         ImageAdapter(WallpaperChooser context) {
@@ -179,7 +251,7 @@
             } else {
                 image = (ImageView) convertView;
             }
-            
+
             int thumbRes = mThumbs.get(position);
             image.setImageResource(thumbRes);
             Drawable thumbDrawable = image.getDrawable();
@@ -189,14 +261,11 @@
                 Log.e(TAG, "Error decoding thumbnail resId=" + thumbRes + " for wallpaper #"
                         + position);
             }
+
             return image;
         }
     }
 
-    public void onClick(View v) {
-        selectWallpaper(mGallery.getSelectedItemPosition());
-    }
-
     class WallpaperLoader extends AsyncTask<Integer, Void, Bitmap> {
         BitmapFactory.Options mOptions;
 
@@ -205,7 +274,8 @@
             mOptions.inDither = false;
             mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;            
         }
-        
+
+        @Override
         protected Bitmap doInBackground(Integer... params) {
             if (isCancelled()) return null;
             try {
@@ -225,17 +295,20 @@
                 if (mBitmap != null) {
                     mBitmap.recycle();
                 }
-    
-                final ImageView view = mImageView;
-                view.setImageBitmap(b);
-    
-                mBitmap = b;
-    
-                final Drawable drawable = view.getDrawable();
-                drawable.setFilterBitmap(true);
-                drawable.setDither(true);
 
-                view.postInvalidate();
+                // This should always be the case, but check anyways
+                final ImageView view = mImageView;
+                if (view != null) {
+                    view.setImageBitmap(b);
+
+                    mBitmap = b;
+
+                    final Drawable drawable = view.getDrawable();
+                    drawable.setFilterBitmap(true);
+                    drawable.setDither(true);
+
+                    view.postInvalidate();
+                }
 
                 mLoader = null;
             } else {
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index f9d087d..0a3f915 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -109,6 +109,7 @@
     private Drawable mBackground;
     private float mBackgroundAlpha = 0;
     private float mOverScrollMaxBackgroundAlpha = 0.0f;
+    private int mOverScrollPageIndex = -1;
 
     private final WallpaperManager mWallpaperManager;
 
@@ -268,9 +269,9 @@
     @Override
     protected int getScrollMode() {
         if (LauncherApplication.isScreenXLarge()) {
-            return SmoothPagedView.QUINTIC_MODE;
+            return SmoothPagedView.X_LARGE_MODE;
         } else {
-            return SmoothPagedView.OVERSHOOT_MODE;
+            return SmoothPagedView.DEFAULT_MODE;
         }
     }
 
@@ -516,6 +517,7 @@
             mAnimOnPageEndMoving = null;
         }
         mOverScrollMaxBackgroundAlpha = 0.0f;
+        mOverScrollPageIndex = -1;
         mPageMoving = false;
     }
 
@@ -653,7 +655,7 @@
     }
 
     float overScrollBackgroundAlphaInterpolator(float r) {
-        float threshold = 0.1f;
+        float threshold = 0.08f;
 
         if (r > mOverScrollMaxBackgroundAlpha) {
             mOverScrollMaxBackgroundAlpha = r;
@@ -664,23 +666,6 @@
         return Math.min(r / threshold, 1.0f);
     }
 
-    protected void overScroll(float amount) {
-        final int lastChildIndex = getChildCount() - 1;
-
-        CellLayout cl;
-        if (amount < 0) {
-            cl = (CellLayout) getChildAt(0);
-        } else {
-            cl = (CellLayout) getChildAt(lastChildIndex);
-        }
-
-        final int totalDistance = cl.getMeasuredWidth() + mPageSpacing;
-        float r = 1.0f * amount / totalDistance;
-        float rotation = -WORKSPACE_ROTATION * r;
-        cl.setBackgroundAlphaMultiplier(overScrollBackgroundAlphaInterpolator(Math.abs(r)));
-        cl.setRotationY(rotation);
-    }
-
     @Override
     protected void screenScrolled(int screenCenter) {
         final int halfScreenSize = getMeasuredWidth() / 2;
@@ -696,7 +681,18 @@
                 scrollProgress = Math.min(scrollProgress, 1.0f);
                 scrollProgress = Math.max(scrollProgress, -1.0f);
 
-                cl.setBackgroundAlphaMultiplier(backgroundAlphaInterpolator(Math.abs(scrollProgress)));
+                // If the current page (i) is being overscrolled, we use a different
+                // set of rules for setting the background alpha multiplier.
+                if ((mScrollX < 0 && i == 0) || (mScrollX > mMaxScrollX &&
+                        i == getChildCount() -1 )) {
+                    cl.setBackgroundAlphaMultiplier(
+                            overScrollBackgroundAlphaInterpolator(Math.abs(scrollProgress)));
+                    mOverScrollPageIndex = i;
+                } else if (mOverScrollPageIndex != i) {
+                    cl.setBackgroundAlphaMultiplier(
+                            backgroundAlphaInterpolator(Math.abs(scrollProgress)));
+
+                }
 
                 float rotation = WORKSPACE_ROTATION * scrollProgress;
                 float translationX = getOffsetXForRotation(rotation, cl.getWidth(), cl.getHeight());
@@ -1168,24 +1164,14 @@
         updateWhichPagesAcceptDrops(mShrunkenState);
     }
 
-    // We call this when we trigger an unshrink by clicking on the CellLayout cl
-    public void unshrink(CellLayout clThatWasClicked) {
-        int newCurrentPage = mCurrentPage;
-        final int screenCount = getChildCount();
-        for (int i = 0; i < screenCount; i++) {
-            if (getChildAt(i) == clThatWasClicked) {
-                newCurrentPage = i;
-            }
-        }
-        unshrink(newCurrentPage);
-    }
-
     @Override
     protected boolean handlePagingClicks() {
         return true;
     }
 
-    private void unshrink(int newCurrentPage) {
+    // We call this when we trigger an unshrink by clicking on the CellLayout cl
+    public void unshrink(CellLayout clThatWasClicked) {
+        int newCurrentPage = indexOfChild(clThatWasClicked);
         if (mIsSmall) {
             int newX = getChildOffset(newCurrentPage) - getRelativeChildOffset(newCurrentPage);
             int delta = newX - mScrollX;
@@ -1360,7 +1346,6 @@
         CellLayout current = getCurrentDropLayout();
 
         current.onDragChild(child);
-        child.setVisibility(View.GONE);
 
         child.clearFocus();
         child.setPressed(false);
@@ -1406,8 +1391,8 @@
         // Based on the position of the drag view, find the top left of the original view
         int viewX = dragViewX + (dragView.getWidth() - child.getWidth()) / 2;
         int viewY = dragViewY + (dragView.getHeight() - child.getHeight()) / 2;
-        viewX -= getResources().getInteger(R.integer.config_dragViewOffsetX);
-        viewY -= getResources().getInteger(R.integer.config_dragViewOffsetY);
+        viewX += getResources().getInteger(R.integer.config_dragViewOffsetX);
+        viewY += getResources().getInteger(R.integer.config_dragViewOffsetY);
 
         // Set its old pos (in the new parent's coordinates); it will be animated
         // in animateViewIntoPosition after the next layout pass
@@ -1494,7 +1479,7 @@
         // If it's an external drop (e.g. from All Apps), check if it should be accepted
         if (source != this) {
             // Don't accept the drop if we're not over a screen at time of drop
-            if (mDragTargetLayout == null) {
+            if (mDragTargetLayout == null || !mDragTargetLayout.getAcceptsDrops()) {
                 return false;
             }
 
@@ -1529,7 +1514,13 @@
         }
 
         if (source != this) {
-            onDropExternal(originX, originY, dragInfo, mDragTargetLayout);
+            if (mIsSmall) {
+                // if we drag and drop to small screens, don't pass the touch x/y coords (when we
+                // enable spring-loaded adding, however, we do want to pass the touch x/y coords)
+                onDropExternal(-1, -1, dragInfo, mDragTargetLayout);
+            } else {
+                onDropExternal(originX, originY, dragInfo, mDragTargetLayout);
+            }
         } else if (mDragInfo != null) {
             final View cell = mDragInfo.cell;
             CellLayout dropTargetLayout = mDragTargetLayout;
@@ -1936,7 +1927,7 @@
                         mDragTargetLayout.setHover(false);
                     }
                     mDragTargetLayout = layout;
-                    if (mDragTargetLayout != null) {
+                    if (mDragTargetLayout != null && mDragTargetLayout.getAcceptsDrops()) {
                         mDragTargetLayout.setHover(true);
                     }
                 }
@@ -2046,6 +2037,7 @@
             PendingAddItemInfo info = (PendingAddItemInfo) dragInfo;
             // When dragging and dropping from customization tray, we deal with creating
             // widgets/shortcuts/folders in a slightly different way
+            // Only set touchXY if you are supporting spring loaded adding of items
             int[] touchXY = new int[2];
             touchXY[0] = x;
             touchXY[1] = y;
@@ -2064,38 +2056,37 @@
             }
             cellLayout.onDragExit();
             cellLayout.animateDrop();
-            return;
-        }
-
-        // This is for other drag/drop cases, like dragging from All Apps
-        ItemInfo info = (ItemInfo) dragInfo;
-
-        View view = null;
-
-        switch (info.itemType) {
-        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
-        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-            if (info.container == NO_ID && info instanceof ApplicationInfo) {
-                // Came from all apps -- make a copy
-                info = new ShortcutInfo((ApplicationInfo) info);
-            }
-            view = mLauncher.createShortcut(R.layout.application, cellLayout,
-                    (ShortcutInfo) info);
-            break;
-        case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
-            view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
-                    cellLayout, (UserFolderInfo) info, mIconCache);
-            break;
-        default:
-            throw new IllegalStateException("Unknown item type: " + info.itemType);
-        }
-
-        // If the view is null, it has already been added.
-        if (view == null) {
-            cellLayout.onDragExit();
         } else {
-            mTargetCell = new int[]{x, y};
-            cellLayout.findCellForSpan(mTargetCell, 1, 1);
+            // This is for other drag/drop cases, like dragging from All Apps
+            ItemInfo info = (ItemInfo) dragInfo;
+
+            View view = null;
+
+            switch (info.itemType) {
+            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                if (info.container == NO_ID && info instanceof ApplicationInfo) {
+                    // Came from all apps -- make a copy
+                    info = new ShortcutInfo((ApplicationInfo) info);
+                }
+                view = mLauncher.createShortcut(R.layout.application, cellLayout,
+                        (ShortcutInfo) info);
+                break;
+            case LauncherSettings.Favorites.ITEM_TYPE_USER_FOLDER:
+                view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher,
+                        cellLayout, (UserFolderInfo) info, mIconCache);
+                break;
+            default:
+                throw new IllegalStateException("Unknown item type: " + info.itemType);
+            }
+
+            mTargetCell = new int[2];
+            if (x != -1 && y != -1) {
+                // when dragging and dropping, just find the closest free spot
+                cellLayout.findNearestVacantArea(x, y, 1, 1, mTargetCell);
+            } else {
+                cellLayout.findCellForSpan(mTargetCell, 1, 1);
+            }
             addInScreen(view, indexOfChild(cellLayout), mTargetCell[0],
                     mTargetCell[1], info.spanX, info.spanY, insertAtFirst);
             cellLayout.onDropChild(view);
@@ -2176,6 +2167,11 @@
         mDragInfo = null;
     }
 
+    @Override
+    public void onDragViewVisible() {
+        ((View) mDragInfo.cell).setVisibility(View.GONE);
+    }
+
     public boolean isDropEnabled() {
         return true;
     }