Merge "Fix NPE that occurs with poorly behaved widgets" into ics-mr1
diff --git a/res/drawable-hdpi/ic_home_all_apps_holo_dark.png b/res/drawable-hdpi/ic_home_all_apps_holo_dark.png
index a0bfc0f..18a524b 100644
--- a/res/drawable-hdpi/ic_home_all_apps_holo_dark.png
+++ b/res/drawable-hdpi/ic_home_all_apps_holo_dark.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_clear_active_holo.png b/res/drawable-hdpi/ic_launcher_clear_active_holo.png
new file mode 100644
index 0000000..cdd0052
--- /dev/null
+++ b/res/drawable-hdpi/ic_launcher_clear_active_holo.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_clear_normal_holo.png b/res/drawable-hdpi/ic_launcher_clear_normal_holo.png
new file mode 100644
index 0000000..84549ff
--- /dev/null
+++ b/res/drawable-hdpi/ic_launcher_clear_normal_holo.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_home_all_apps_holo_dark.png b/res/drawable-mdpi/ic_home_all_apps_holo_dark.png
index ffc3020..23616d4 100644
--- a/res/drawable-mdpi/ic_home_all_apps_holo_dark.png
+++ b/res/drawable-mdpi/ic_home_all_apps_holo_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_clear_active_holo.png b/res/drawable-mdpi/ic_launcher_clear_active_holo.png
new file mode 100644
index 0000000..2683bea
--- /dev/null
+++ b/res/drawable-mdpi/ic_launcher_clear_active_holo.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_clear_normal_holo.png b/res/drawable-mdpi/ic_launcher_clear_normal_holo.png
new file mode 100644
index 0000000..219f3e5
--- /dev/null
+++ b/res/drawable-mdpi/ic_launcher_clear_normal_holo.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_home_all_apps_holo_dark.png b/res/drawable-xhdpi/ic_home_all_apps_holo_dark.png
index 21a51ab..dff3e16 100644
--- a/res/drawable-xhdpi/ic_home_all_apps_holo_dark.png
+++ b/res/drawable-xhdpi/ic_home_all_apps_holo_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_clear_active_holo.png b/res/drawable-xhdpi/ic_launcher_clear_active_holo.png
new file mode 100644
index 0000000..1a7e53d
--- /dev/null
+++ b/res/drawable-xhdpi/ic_launcher_clear_active_holo.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_clear_normal_holo.png b/res/drawable-xhdpi/ic_launcher_clear_normal_holo.png
new file mode 100644
index 0000000..d4965d9
--- /dev/null
+++ b/res/drawable-xhdpi/ic_launcher_clear_normal_holo.png
Binary files differ
diff --git a/res/drawable/remove_target_selector.xml b/res/drawable/remove_target_selector.xml
new file mode 100644
index 0000000..5e071fb
--- /dev/null
+++ b/res/drawable/remove_target_selector.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2007, 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.
+*/
+-->
+
+<transition xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/ic_launcher_clear_normal_holo"  />
+    <item android:drawable="@drawable/ic_launcher_clear_active_holo"  />
+</transition>
diff --git a/res/drawable/delete_target_selector.xml b/res/drawable/uninstall_target_selector.xml
similarity index 100%
rename from res/drawable/delete_target_selector.xml
rename to res/drawable/uninstall_target_selector.xml
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 084b163..a74eb14 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -66,6 +66,11 @@
         <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
     </com.android.launcher2.Workspace>
 
+    <include layout="@layout/hotseat"
+        android:id="@+id/hotseat"
+        android:layout_width="@dimen/button_bar_height_plus_padding"
+        android:layout_height="match_parent"
+        android:layout_gravity="right" />
     <include
         android:id="@+id/qsb_bar"
         layout="@layout/qsb_bar" />
@@ -76,12 +81,6 @@
         android:layout_height="match_parent"
         android:visibility="invisible" />
 
-    <include layout="@layout/hotseat"
-        android:id="@+id/hotseat"
-        android:layout_width="@dimen/button_bar_height_plus_padding"
-        android:layout_height="match_parent"
-        android:layout_gravity="right" />
-
     <include layout="@layout/workspace_cling"
         android:id="@+id/workspace_cling"
         android:layout_width="match_parent"
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index b8df2ee..062656f 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -60,16 +60,16 @@
         <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
     </com.android.launcher2.Workspace>
 
-    <include
-        android:id="@+id/qsb_bar"
-        layout="@layout/qsb_bar" />
-
     <include layout="@layout/hotseat"
         android:id="@+id/hotseat"
         android:layout_width="match_parent"
         android:layout_height="@dimen/button_bar_height_plus_padding"
         android:layout_gravity="bottom" />
 
+    <include
+        android:id="@+id/qsb_bar"
+        layout="@layout/qsb_bar" />
+
     <include layout="@layout/apps_customize_pane"
         android:id="@+id/apps_customize_pane"
         android:layout_width="match_parent"
diff --git a/res/layout/apps_customize_widget.xml b/res/layout/apps_customize_widget.xml
index 704401a..90883c5 100644
--- a/res/layout/apps_customize_widget.xml
+++ b/res/layout/apps_customize_widget.xml
@@ -63,7 +63,7 @@
     </LinearLayout>
 
     <!-- The icon of the widget. -->
-    <ImageView
+    <com.android.launcher2.PagedViewWidgetImageView
         android:id="@+id/widget_preview"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/res/layout/qsb_bar.xml b/res/layout/qsb_bar.xml
index c2179ce..9daf7bf 100644
--- a/res/layout/qsb_bar.xml
+++ b/res/layout/qsb_bar.xml
@@ -37,7 +37,7 @@
                 style="@style/DropTargetButton"
                 android:id="@+id/delete_target_text"
                 android:text="@string/delete_zone_label_workspace"
-                android:drawableLeft="@drawable/delete_target_selector" />
+                android:drawableLeft="@drawable/remove_target_selector" />
         </FrameLayout>
 
         <FrameLayout
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index 22f2692..cde0b4b 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -31,13 +31,13 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
 import android.graphics.MaskFilter;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.graphics.Bitmap.Config;
 import android.graphics.TableMaskFilter;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
@@ -48,10 +48,10 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
 import android.widget.GridLayout;
 import android.widget.ImageView;
 import android.widget.Toast;
@@ -984,7 +984,6 @@
 
         // Generate a preview image if we couldn't load one
         if (drawable == null) {
-            Resources resources = mLauncher.getResources();
             // TODO: This actually uses the apps customize cell layout params, where as we make want
             // the Workspace params for more accuracy.
             int targetWidth = mWidgetSpacingLayout.estimateCellWidth(cellHSpan);
@@ -1026,6 +1025,7 @@
                 int hoffset = (int) (bitmapWidth / 2 - mAppIconSize * iconScale / 2);
                 int yoffset = (int) (bitmapHeight / 2 - mAppIconSize * iconScale / 2);
                 if (info.icon > 0) icon = mIconCache.getFullResIcon(packageName, info.icon);
+                Resources resources = mLauncher.getResources();
                 if (icon == null) icon = resources.getDrawable(R.drawable.ic_launcher_application);
 
                 renderDrawableToBitmap(icon, preview, hoffset, yoffset,
@@ -1272,7 +1272,7 @@
                         scale = 1.0f;
                         alpha = 1.0f;
                         // On the first page, we don't want the page to have any lateral motion
-                        translationX = getScrollX();
+                        translationX = 0;
                     } else if (i == getChildCount() - 1 && scrollProgress > 0) {
                         // Overscroll to the right
                         v.setPivotX((1 - TRANSITION_PIVOT) * pageWidth);
@@ -1280,7 +1280,7 @@
                         scale = 1.0f;
                         alpha = 1.0f;
                         // On the last page, we don't want the page to have any lateral motion.
-                        translationX =  getScrollX() - mMaxScrollX;
+                        translationX = 0;
                     } else {
                         v.setPivotY(pageHeight / 2.0f);
                         v.setPivotX(pageWidth / 2.0f);
@@ -1292,6 +1292,14 @@
                 v.setScaleX(scale);
                 v.setScaleY(scale);
                 v.setAlpha(alpha);
+
+                // If the view has 0 alpha, we set it to be invisible so as to prevent
+                // it from accepting touches
+                if (alpha < ViewConfiguration.ALPHA_THRESHOLD) {
+                    v.setVisibility(INVISIBLE);
+                } else if (v.getVisibility() != VISIBLE) {
+                    v.setVisibility(VISIBLE);
+                }
             }
         }
     }
@@ -1428,6 +1436,7 @@
         ApplicationInfo.dumpApplicationInfoList(LOG_TAG, "mApps", mApps);
         dumpAppWidgetProviderInfoList(LOG_TAG, "mWidgets", mWidgets);
     }
+
     private void dumpAppWidgetProviderInfoList(String tag, String label,
             ArrayList<Object> list) {
         Log.d(tag, label + " size=" + list.size());
@@ -1445,6 +1454,7 @@
             }
         }
     }
+
     @Override
     public void surrender() {
         // TODO: If we are in the middle of any process (ie. for holographic outlines, etc) we
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index 9ffc1d0..6e60280 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -36,7 +36,6 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.graphics.Region;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.NinePatchDrawable;
 import android.util.AttributeSet;
@@ -72,6 +71,7 @@
     private int mWidthGap;
     private int mHeightGap;
     private int mMaxGap;
+    private boolean mScrollingTransformsDirty = false;
 
     private final Rect mRect = new Rect();
     private final CellInfo mCellInfo = new CellInfo();
@@ -102,9 +102,6 @@
     private Drawable mOverScrollRight;
     private Rect mBackgroundRect;
     private Rect mForegroundRect;
-    private Rect mGlowBackgroundRect;
-    private float mGlowBackgroundScale;
-    private float mGlowBackgroundAlpha;
     private int mForegroundPadding;
 
     // If we're actively dragging something over this screen, mIsDragOverlapping is true
@@ -258,9 +255,6 @@
 
         mBackgroundRect = new Rect();
         mForegroundRect = new Rect();
-        mGlowBackgroundRect = new Rect();
-        setHoverScale(1.0f);
-        setHoverAlpha(1.0f);
 
         mChildren = new CellLayoutChildren(context);
         mChildren.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap);
@@ -351,68 +345,23 @@
         return mIsDragOverlapping;
     }
 
-    private void updateGlowRect() {
-        float marginFraction = (mGlowBackgroundScale - 1.0f) / 2.0f;
-        int marginX = (int) (marginFraction * (mBackgroundRect.right - mBackgroundRect.left));
-        int marginY = (int) (marginFraction * (mBackgroundRect.bottom - mBackgroundRect.top));
-        mGlowBackgroundRect.set(mBackgroundRect.left - marginX, mBackgroundRect.top - marginY,
-                mBackgroundRect.right + marginX, mBackgroundRect.bottom + marginY);
-        invalidate();
+    protected void setOverscrollTransformsDirty(boolean dirty) {
+        mScrollingTransformsDirty = dirty;
     }
 
-    public void setHoverScale(float scaleFactor) {
-        if (scaleFactor != mGlowBackgroundScale) {
-            mGlowBackgroundScale = scaleFactor;
-            updateGlowRect();
-            if (getParent() != null) {
-                ((View) getParent()).invalidate();
-            }
+    protected void resetOverscrollTransforms() {
+        if (mScrollingTransformsDirty) {
+            setOverscrollTransformsDirty(false);
+            setTranslationX(0);
+            setRotationY(0);
+            // It doesn't matter if we pass true or false here, the important thing is that we
+            // pass 0, which results in the overscroll drawable not being drawn any more.
+            setOverScrollAmount(0, false);
+            setPivotX(getMeasuredWidth() / 2);
+            setPivotY(getMeasuredHeight() / 2);
         }
     }
 
-    public float getHoverScale() {
-        return mGlowBackgroundScale;
-    }
-
-    public float getHoverAlpha() {
-        return mGlowBackgroundAlpha;
-    }
-
-    public void setHoverAlpha(float alpha) {
-        mGlowBackgroundAlpha = alpha;
-        invalidate();
-    }
-
-    void animateDrop() {
-        Resources res = getResources();
-        float onDropScale = res.getInteger(R.integer.config_screenOnDropScalePercent) / 100.0f;
-        ObjectAnimator scaleUp = ObjectAnimator.ofFloat(this, "hoverScale", onDropScale);
-        scaleUp.setDuration(res.getInteger(R.integer.config_screenOnDropScaleUpDuration));
-        ObjectAnimator scaleDown = ObjectAnimator.ofFloat(this, "hoverScale", 1.0f);
-        scaleDown.setDuration(res.getInteger(R.integer.config_screenOnDropScaleDownDuration));
-        ObjectAnimator alphaFadeOut = ObjectAnimator.ofFloat(this, "hoverAlpha", 0.0f);
-
-        alphaFadeOut.setStartDelay(res.getInteger(R.integer.config_screenOnDropAlphaFadeDelay));
-        alphaFadeOut.setDuration(res.getInteger(R.integer.config_screenOnDropAlphaFadeDuration));
-
-        AnimatorSet bouncer = new AnimatorSet();
-        bouncer.play(scaleUp).before(scaleDown);
-        bouncer.play(scaleUp).with(alphaFadeOut);
-        bouncer.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                setIsDragOverlapping(true);
-            }
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                setIsDragOverlapping(false);
-                setHoverScale(1.0f);
-                setHoverAlpha(1.0f);
-            }
-        });
-        bouncer.start();
-    }
-
     @Override
     protected void onDraw(Canvas canvas) {
         // When we're large, we are either drawn in a "hover" state (ie when dragging an item to
@@ -939,7 +888,6 @@
         mBackgroundRect.set(0, 0, w, h);
         mForegroundRect.set(mForegroundPadding, mForegroundPadding,
                 w - 2 * mForegroundPadding, h - 2 * mForegroundPadding);
-        updateGlowRect();
     }
 
     @Override
diff --git a/src/com/android/launcher2/DeleteDropTarget.java b/src/com/android/launcher2/DeleteDropTarget.java
index 1553d3c..4e93b22 100644
--- a/src/com/android/launcher2/DeleteDropTarget.java
+++ b/src/com/android/launcher2/DeleteDropTarget.java
@@ -23,6 +23,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.graphics.drawable.TransitionDrawable;
 import android.util.AttributeSet;
 import android.view.View;
@@ -35,8 +36,10 @@
 
     private static int DELETE_ANIMATION_DURATION = 250;
     private ColorStateList mOriginalTextColor;
-    private TransitionDrawable mDrawable;
     private int mHoverColor = 0xFFFF0000;
+    private TransitionDrawable mUninstallDrawable;
+    private TransitionDrawable mRemoveDrawable;
+    private TransitionDrawable mCurrentDrawable;
 
     public DeleteDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -58,8 +61,16 @@
         mHoverColor = r.getColor(R.color.delete_target_hover_tint);
         mHoverPaint.setColorFilter(new PorterDuffColorFilter(
                 mHoverColor, PorterDuff.Mode.SRC_ATOP));
-        mDrawable = (TransitionDrawable) getCompoundDrawables()[0];
-        mDrawable.setCrossFadeEnabled(true);
+        mUninstallDrawable = (TransitionDrawable) 
+                r.getDrawable(R.drawable.uninstall_target_selector);
+        mRemoveDrawable = (TransitionDrawable) r.getDrawable(R.drawable.remove_target_selector);
+
+        mRemoveDrawable.setCrossFadeEnabled(true);
+        mUninstallDrawable.setCrossFadeEnabled(true);
+
+        // The current drawable is set to either the remove drawable or the uninstall drawable 
+        // and is initially set to the remove drawable, as set in the layout xml.
+        mCurrentDrawable = (TransitionDrawable) getCompoundDrawables()[0];
 
         // Remove the text in the Phone UI in landscape
         int orientation = getResources().getConfiguration().orientation;
@@ -116,8 +127,15 @@
             }
         }
 
+        if (isUninstall) {
+            setCompoundDrawablesWithIntrinsicBounds(mUninstallDrawable, null, null, null);
+        } else {
+            setCompoundDrawablesWithIntrinsicBounds(mRemoveDrawable, null, null, null);
+        }
+        mCurrentDrawable = (TransitionDrawable) getCompoundDrawables()[0];
+
         mActive = isVisible;
-        mDrawable.resetTransition();
+        mCurrentDrawable.resetTransition();
         setTextColor(mOriginalTextColor);
         ((ViewGroup) getParent()).setVisibility(isVisible ? View.VISIBLE : View.GONE);
         if (getText().length() > 0) {
@@ -135,7 +153,7 @@
     public void onDragEnter(DragObject d) {
         super.onDragEnter(d);
 
-        mDrawable.startTransition(mTransitionDuration);
+        mCurrentDrawable.startTransition(mTransitionDuration);
         setTextColor(mHoverColor);
     }
 
@@ -143,7 +161,7 @@
         super.onDragExit(d);
 
         if (!d.dragComplete) {
-            mDrawable.resetTransition();
+            mCurrentDrawable.resetTransition();
             setTextColor(mOriginalTextColor);
         }
     }
@@ -155,8 +173,8 @@
         dragLayer.getViewRectRelativeToSelf(d.dragView, from);
         dragLayer.getViewRectRelativeToSelf(this, to);
 
-        int width = mDrawable.getIntrinsicWidth();
-        int height = mDrawable.getIntrinsicHeight();
+        int width = mCurrentDrawable.getIntrinsicWidth();
+        int height = mCurrentDrawable.getIntrinsicHeight();
         to.set(to.left + getPaddingLeft(), to.top + getPaddingTop(),
                 to.left + getPaddingLeft() + width, to.bottom);
 
diff --git a/src/com/android/launcher2/DragLayer.java b/src/com/android/launcher2/DragLayer.java
index 433db50..3c626d4 100644
--- a/src/com/android/launcher2/DragLayer.java
+++ b/src/com/android/launcher2/DragLayer.java
@@ -70,7 +70,7 @@
     private boolean mHoverPointClosesFolder = false;
     private Rect mHitRect = new Rect();
     private int mWorkspaceIndex = -1;
-    private int mHotseatIndex = -1;
+    private int mQsbIndex = -1;
 
     /**
      * Used to create a new DragLayer from XML.
@@ -627,13 +627,13 @@
     private void updateChildIndices() {
         if (mLauncher != null) {
             mWorkspaceIndex = indexOfChild(mLauncher.getWorkspace());
-            mHotseatIndex = indexOfChild(mLauncher.getHotseat());
+            mQsbIndex = indexOfChild(mLauncher.getSearchBar());
         }
     }
 
     @Override
     protected int getChildDrawingOrder(int childCount, int i) {
-        if (mWorkspaceIndex == -1 || mHotseatIndex == -1 || 
+        if (mWorkspaceIndex == -1 || mQsbIndex == -1 || 
                 mLauncher.getWorkspace().isDrawingBackgroundGradient()) {
             return i;
         }
@@ -641,10 +641,10 @@
         // This ensures that the workspace is drawn above the hotseat and qsb,
         // except when the workspace is drawing a background gradient, in which
         // case we want the workspace to stay behind these elements.
-        if (i == mHotseatIndex) {
+        if (i == mQsbIndex) {
             return mWorkspaceIndex;
         } else if (i == mWorkspaceIndex) {
-            return mHotseatIndex;
+            return mQsbIndex;
         } else {
             return i;
         }
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index a62dfa6..f91a471 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -1956,6 +1956,9 @@
     Hotseat getHotseat() {
         return mHotseat;
     }
+    SearchDropTargetBar getSearchBar() {
+        return mSearchDropTargetBar;
+    }
 
     /**
      * Returns the CellLayout of the specified container at the specified screen.
@@ -2503,8 +2506,6 @@
     void addExternalItemToScreen(ItemInfo itemInfo, final CellLayout layout) {
         if (!mWorkspace.addExternalItemToScreen(itemInfo, layout)) {
             showOutOfSpaceMessage();
-        } else {
-            layout.animateDrop();
         }
     }
 
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index 2de7d4a..14ef53f 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -127,6 +127,12 @@
     protected boolean mCenterPagesVertically;
     protected boolean mAllowOverScroll = true;
     protected int mUnboundedScrollX;
+    protected int[] mTempVisiblePagesRange = new int[2];
+
+    // mOverScrollX is equal to mScrollX when we're within the normal scroll range. Otherwise
+    // it is equal to the scaled overscroll position. We use a separate value so as to prevent
+    // the screens from continuing to translate beyond the normal bounds.
+    protected int mOverScrollX;
 
     // parameter that adjusts the layout to be optimized for pages with that scale factor
     protected float mLayoutScale = 1.0f;
@@ -376,6 +382,7 @@
                 overScroll(x - mMaxScrollX);
             }
         } else {
+            mOverScrollX = x;
             super.scrollTo(x, y);
         }
 
@@ -701,20 +708,7 @@
         return (int) (maxWidth * mLayoutScale + 0.5f);
     }
 
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        int halfScreenSize = getMeasuredWidth() / 2;
-        int screenCenter = mScrollX + halfScreenSize;
-
-        if (screenCenter != mLastScreenCenter || mForceScreenScrolled) {
-            screenScrolled(screenCenter);
-            mLastScreenCenter = screenCenter;
-            mForceScreenScrolled = false;
-        }
-
-        // Find out which screens are visible; as an optimization we only call draw on them
-        // As an optimization, this code assumes that all pages have the same width as the 0th
-        // page.
+    protected void getVisiblePages(int[] range) {
         final int pageCount = getChildCount();
         if (pageCount > 0) {
             final int pageWidth = getScaledMeasuredWidth(getPageAt(0));
@@ -731,6 +725,33 @@
                 rightScreen++;
                 x += getScaledMeasuredWidth(getPageAt(rightScreen)) + mPageSpacing;
             }
+            range[0] = leftScreen;
+            range[1] = rightScreen;
+        } else {
+            range[0] = -1;
+            range[1] = -1;
+        }
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        int halfScreenSize = getMeasuredWidth() / 2;
+        // mOverScrollX is equal to mScrollX when we're within the normal scroll range. Otherwise
+        // it is equal to the scaled overscroll position.
+        int screenCenter = mOverScrollX + halfScreenSize;
+
+        if (screenCenter != mLastScreenCenter || mForceScreenScrolled) {
+            screenScrolled(screenCenter);
+            mLastScreenCenter = screenCenter;
+            mForceScreenScrolled = false;
+        }
+
+        // Find out which screens are visible; as an optimization we only call draw on them
+        final int pageCount = getChildCount();
+        if (pageCount > 0) {
+            getVisiblePages(mTempVisiblePagesRange);
+            final int leftScreen = mTempVisiblePagesRange[0];
+            final int rightScreen = mTempVisiblePagesRange[1];
 
             final long drawingTime = getDrawingTime();
             // Clip to the bounds
@@ -1064,9 +1085,11 @@
 
         int overScrollAmount = (int) Math.round(f * screenSize);
         if (amount < 0) {
-            mScrollX = overScrollAmount;
+            mOverScrollX = overScrollAmount;
+            mScrollX = 0;
         } else {
-            mScrollX = mMaxScrollX + overScrollAmount;
+            mOverScrollX = mMaxScrollX + overScrollAmount;
+            mScrollX = mMaxScrollX;
         }
         invalidate();
     }
@@ -1086,9 +1109,11 @@
 
         int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
         if (amount < 0) {
-            mScrollX = overScrollAmount;
+            mOverScrollX = overScrollAmount;
+            mScrollX = 0;
         } else {
-            mScrollX = mMaxScrollX + overScrollAmount;
+            mOverScrollX = mMaxScrollX + overScrollAmount;
+            mScrollX = mMaxScrollX;
         }
         invalidate();
     }
@@ -1777,7 +1802,7 @@
             updateScrollingIndicatorPosition();
             cancelScrollingIndicatorAnimations();
             if (immediately) {
-                mScrollIndicator.setVisibility(View.GONE);
+                mScrollIndicator.setVisibility(View.INVISIBLE);
                 mScrollIndicator.setAlpha(0f);
             } else {
                 mScrollIndicatorAnimator = ObjectAnimator.ofFloat(mScrollIndicator, "alpha", 0f);
@@ -1791,7 +1816,7 @@
                     @Override
                     public void onAnimationEnd(Animator animation) {
                         if (!cancelled) {
-                            mScrollIndicator.setVisibility(View.GONE);
+                            mScrollIndicator.setVisibility(View.INVISIBLE);
                         }
                     }
                 });
diff --git a/src/com/android/launcher2/PagedViewWidget.java b/src/com/android/launcher2/PagedViewWidget.java
index 12c98b2..3eb4db4 100644
--- a/src/com/android/launcher2/PagedViewWidget.java
+++ b/src/com/android/launcher2/PagedViewWidget.java
@@ -136,10 +136,13 @@
     }
 
     void applyPreview(FastBitmapDrawable preview, int index, boolean scale) {
-        final ImageView image = (ImageView) findViewById(R.id.widget_preview);
+        final PagedViewWidgetImageView image =
+                (PagedViewWidgetImageView) findViewById(R.id.widget_preview);
         if (preview != null) {
+            image.mAllowRequestLayout = false;
             image.setImageDrawable(preview);
             image.setScaleType(scale ? ImageView.ScaleType.FIT_START : ImageView.ScaleType.MATRIX);
+            image.mAllowRequestLayout = true;
             image.setAlpha(0f);
             image.animate()
                  .alpha(1f)
diff --git a/src/com/android/launcher2/PagedViewWidgetImageView.java b/src/com/android/launcher2/PagedViewWidgetImageView.java
new file mode 100644
index 0000000..844b337
--- /dev/null
+++ b/src/com/android/launcher2/PagedViewWidgetImageView.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher2;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+
+
+class PagedViewWidgetImageView extends ImageView {
+    public boolean mAllowRequestLayout = true;
+
+    public PagedViewWidgetImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void requestLayout() {
+        if (mAllowRequestLayout) {
+            super.requestLayout();
+        }
+    }
+}
diff --git a/src/com/android/launcher2/SearchDropTargetBar.java b/src/com/android/launcher2/SearchDropTargetBar.java
index e90406e..3a7f24b 100644
--- a/src/com/android/launcher2/SearchDropTargetBar.java
+++ b/src/com/android/launcher2/SearchDropTargetBar.java
@@ -119,7 +119,7 @@
         mDropTargetBarFadeOutAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                mDropTargetBar.setVisibility(View.GONE);
+                mDropTargetBar.setVisibility(View.INVISIBLE);
                 mDropTargetBar.setLayerType(View.LAYER_TYPE_NONE, null);
             }
         });
@@ -136,7 +136,7 @@
         mQSBSearchBarFadeOutAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
-                mQSBSearchBar.setVisibility(View.GONE);
+                mQSBSearchBar.setVisibility(View.INVISIBLE);
             }
         });
     }
@@ -166,7 +166,7 @@
         if (animated) {
             mQSBSearchBarFadeOutAnim.start();
         } else {
-            mQSBSearchBar.setVisibility(View.GONE);
+            mQSBSearchBar.setVisibility(View.INVISIBLE);
             mQSBSearchBar.setAlpha(0f);
         }
         mIsSearchBarHidden = true;
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index d3a31c4..4ad441d 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -1101,6 +1101,7 @@
     }
 
     private void screenScrolledLargeUI(int screenCenter) {
+        boolean isInOverscroll = false;
         for (int i = 0; i < getChildCount(); i++) {
             CellLayout cl = (CellLayout) getChildAt(i);
             if (cl != null) {
@@ -1111,11 +1112,17 @@
                 // If the current page (i) is being over scrolled, we use a different
                 // set of rules for setting the background alpha multiplier.
                 if (!isSmall()) {
-                    if ((mScrollX < 0 && i == 0) || (mScrollX > mMaxScrollX &&
-                            i == getChildCount() -1 )) {
+                    if ((mOverScrollX < 0 && i == 0) || (mOverScrollX > mMaxScrollX &&
+                            i == getChildCount() -1)) {
+                        isInOverscroll = true;
+                        rotation *= -1;
                         cl.setBackgroundAlphaMultiplier(
                                 overScrollBackgroundAlphaInterpolator(Math.abs(scrollProgress)));
                         mOverScrollPageIndex = i;
+                        cl.setOverScrollAmount(Math.abs(scrollProgress), i == 0);
+                        cl.setPivotX(cl.getMeasuredWidth() * (i == 0 ? 0.75f : 0.25f));
+                        cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
+                        cl.setOverscrollTransformsDirty(true);
                     } else if (mOverScrollPageIndex != i) {
                         cl.setBackgroundAlphaMultiplier(
                                 backgroundAlphaInterpolator(Math.abs(scrollProgress)));
@@ -1130,29 +1137,25 @@
                 cl.fastInvalidate();
             }
         }
+        if (!isSwitchingState() && !isInOverscroll) {
+            ((CellLayout) getChildAt(0)).resetOverscrollTransforms();
+            ((CellLayout) getChildAt(getChildCount() - 1)).resetOverscrollTransforms();
+        }
         invalidate();
     }
 
-    private void resetCellLayoutTransforms(CellLayout cl, boolean left) {
-        cl.setTranslationX(0);
-        cl.setRotationY(0);
-        cl.setOverScrollAmount(0, left);
-        cl.setPivotX(cl.getMeasuredWidth() / 2);
-        cl.setPivotY(cl.getMeasuredHeight() / 2);
-    }
-
     private void screenScrolledStandardUI(int screenCenter) {
-        if (mScrollX < 0 || mScrollX > mMaxScrollX) {
-            int index = mScrollX < 0 ? 0 : getChildCount() - 1;
+        if (mOverScrollX < 0 || mOverScrollX > mMaxScrollX) {
+            int index = mOverScrollX < 0 ? 0 : getChildCount() - 1;
             CellLayout cl = (CellLayout) getChildAt(index);
             float scrollProgress = getScrollProgress(screenCenter, cl, index);
             cl.setOverScrollAmount(Math.abs(scrollProgress), index == 0);
-            float translationX = index == 0 ? mScrollX : - (mMaxScrollX - mScrollX);
             float rotation = - WORKSPACE_OVERSCROLL_ROTATION * scrollProgress;
             cl.setCameraDistance(mDensity * CAMERA_DISTANCE);
             cl.setPivotX(cl.getMeasuredWidth() * (index == 0 ? 0.75f : 0.25f));
-            cl.setTranslationX(translationX);
+            cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
             cl.setRotationY(rotation);
+            cl.setOverscrollTransformsDirty(true);
             setFadeForOverScroll(Math.abs(scrollProgress));
         } else {
             if (mOverscrollFade != 0) {
@@ -1160,8 +1163,8 @@
             }
             // We don't want to mess with the translations during transitions
             if (!isSwitchingState()) {
-                resetCellLayoutTransforms((CellLayout) getChildAt(0), true);
-                resetCellLayoutTransforms((CellLayout) getChildAt(getChildCount() - 1), false);
+                ((CellLayout) getChildAt(0)).resetOverscrollTransforms();
+                ((CellLayout) getChildAt(getChildCount() - 1)).resetOverscrollTransforms();
             }
         }
     }
@@ -1241,6 +1244,19 @@
 
     @Override
     protected void dispatchDraw(Canvas canvas) {
+        if (mChildrenLayersEnabled) {
+            getVisiblePages(mTempVisiblePagesRange);
+            final int leftScreen = mTempVisiblePagesRange[0];
+            final int rightScreen = mTempVisiblePagesRange[1];
+            if (leftScreen != -1 && rightScreen != -1) {
+                // calling setChildrenLayersEnabled on a view that's not visible/rendered
+                // causes slowdowns on some graphics cards, so we set it here to be sure
+                // it's only called when it's safe (ie when the view will be rendered)
+                for (int i = leftScreen; i <= rightScreen; i++) {
+                    ((ViewGroup)getPageAt(i)).setChildrenLayersEnabled(true);
+                }
+            }
+        }
         super.dispatchDraw(canvas);
 
         if (mInScrollArea && !LauncherApplication.isScreenLarge()) {
@@ -1345,8 +1361,13 @@
 
         if (enableChildrenLayers != mChildrenLayersEnabled) {
             mChildrenLayersEnabled = enableChildrenLayers;
-            for (int i = 0; i < getPageCount(); i++) {
-                ((ViewGroup)getChildAt(i)).setChildrenLayersEnabled(enableChildrenLayers);
+            // calling setChildrenLayersEnabled on a view that's not visible/rendered
+            // causes slowdowns on some graphics cards, so we only disable it here and leave
+            // the enabling to dispatchDraw
+            if (!enableChildrenLayers) {
+                for (int i = 0; i < getPageCount(); i++) {
+                    ((ViewGroup)getChildAt(i)).setChildrenLayersEnabled(false);
+                }
             }
         }
     }
@@ -2960,7 +2981,6 @@
             addInScreen(view, container, screen, mTargetCell[0], mTargetCell[1], info.spanX,
                     info.spanY, insertAtFirst);
             cellLayout.onDropChild(view);
-            cellLayout.animateDrop();
             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
             cellLayout.getChildrenLayout().measureChild(view);