Merge "Fix NPE" into jb-ub-now-indigo-rose
diff --git a/Android.mk b/Android.mk
index 016d151..10c9f24 100644
--- a/Android.mk
+++ b/Android.mk
@@ -21,7 +21,13 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src) \
+    $(call all-proto-files-under, protos)
+
+LOCAL_PROTOC_OPTIMIZE_TYPE := nano
+
+LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos
+
 LOCAL_SDK_VERSION := 17
 
 LOCAL_PACKAGE_NAME := Launcher3
diff --git a/protos/backup.proto b/protos/backup.proto
new file mode 100644
index 0000000..3780bc5
--- /dev/null
+++ b/protos/backup.proto
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package launcher_backup;
+
+option java_package = "com.android.launcher3.backup";
+option java_outer_classname = "BackupProtos";
+
+message Key {
+  enum Type {
+    FAVORITE = 1;
+    SCREEN = 2;
+    IMAGE = 3;
+  }
+  required Type type = 1;
+  optional string name = 2;  // keep this short
+  optional int64 id = 3;
+  optional int64 checksum = 4;
+}
+
+message CheckedMessage {
+  required bytes payload = 1;
+  required int64 checksum = 2;
+}
+
+message Journal {
+  required int32 app_version = 1;
+  required int64 t = 2;
+  optional int64 bytes = 3;
+  optional int32 rows = 4;
+  repeated Key key = 5;
+}
+
+message Favorite {
+  required int64 id = 1;
+  required int32 itemType = 2;
+  optional string title = 3;
+  optional int32 container = 4;
+  optional int32 screen = 5;
+  optional int32 cellX = 6;
+  optional int32 cellY = 7;
+  optional int32 spanX = 8;
+  optional int32 spanY = 9;
+  optional int32 displayMode = 10;
+  optional int32 appWidgetId = 11;
+  optional string appWidgetProvider = 12;
+  optional string intent = 13;
+  optional string uri = 14;
+  optional int32 iconType = 15;
+  optional string iconPackage = 16;
+  optional string iconResource = 17;
+  optional bytes icon = 18;
+ }
+
+message Screen {
+  required int64 id = 1;
+  optional int32 rank = 2;
+ }
+
+message Resource {
+  required int32 dpi = 2;
+  required bytes data = 3;
+ }
diff --git a/res/layout-land/first_run_cling.xml b/res/layout-land/first_run_cling.xml
index f827380..3b21e14 100644
--- a/res/layout-land/first_run_cling.xml
+++ b/res/layout-land/first_run_cling.xml
@@ -34,9 +34,10 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_horizontal"
+                android:layout_marginBottom="10dp"
                 android:text="@string/first_run_cling_title"
-                android:textColor="#49C0EC"
-                android:textSize="32sp" />
+                android:textColor="#FFFFFFFF"
+                android:textSize="30sp" />
             <TextView
                 style="@style/ClingAltTitleText"
                 android:layout_width="wrap_content"
@@ -47,22 +48,28 @@
         </LinearLayout>
         <TextView
             style="@style/ClingHintText"
+            android:id="@+id/search_bar_hint"
             android:layout_width="160dp"
             android:layout_height="wrap_content"
             android:layout_gravity="top|end"
             android:layout_marginEnd="10dp"
             android:layout_marginTop="80dp"
-            android:text="@string/first_run_cling_search_bar_hint"
-            android:visibility="gone" />
+            android:visibility="gone"
+            android:drawableTop="@drawable/cling_arrow_up"
+            android:drawablePadding="5dp"
+            android:text="@string/first_run_cling_search_bar_hint" />
         <TextView
             style="@style/ClingHintText"
+            android:id="@+id/custom_content_hint"
             android:layout_width="160dp"
             android:layout_height="wrap_content"
             android:layout_gravity="top"
             android:layout_marginStart="10dp"
             android:layout_marginTop="100dp"
-            android:text="@string/first_run_cling_custom_content_hint"
-            android:visibility="gone" />
+            android:visibility="gone"
+            android:drawableStart="@drawable/cling_arrow_left"
+            android:drawablePadding="10dp"
+            android:text="@string/first_run_cling_custom_content_hint" />
         <TextView
             style="@style/ClingHintText"
             android:layout_width="160dp"
@@ -70,8 +77,9 @@
             android:layout_gravity="bottom|end"
             android:layout_marginEnd="10dp"
             android:layout_marginBottom="100dp"
-            android:text="@string/first_run_cling_create_screens_hint"
-            android:visibility="gone" />
+            android:drawableEnd="@drawable/cling_arrow_right"
+            android:drawablePadding="5dp"
+            android:text="@string/first_run_cling_create_screens_hint" />
     </FrameLayout>
     <Button
         style="@style/ClingButton"
diff --git a/res/layout-port/first_run_cling.xml b/res/layout-port/first_run_cling.xml
index cdc49b9..3b21e14 100644
--- a/res/layout-port/first_run_cling.xml
+++ b/res/layout-port/first_run_cling.xml
@@ -36,7 +36,7 @@
                 android:layout_gravity="center_horizontal"
                 android:layout_marginBottom="10dp"
                 android:text="@string/first_run_cling_title"
-                android:textColor="#49C0EC"
+                android:textColor="#FFFFFFFF"
                 android:textSize="30sp" />
             <TextView
                 style="@style/ClingAltTitleText"
@@ -48,19 +48,27 @@
         </LinearLayout>
         <TextView
             style="@style/ClingHintText"
+            android:id="@+id/search_bar_hint"
             android:layout_width="160dp"
             android:layout_height="wrap_content"
             android:layout_gravity="top|end"
             android:layout_marginEnd="10dp"
             android:layout_marginTop="80dp"
+            android:visibility="gone"
+            android:drawableTop="@drawable/cling_arrow_up"
+            android:drawablePadding="5dp"
             android:text="@string/first_run_cling_search_bar_hint" />
         <TextView
             style="@style/ClingHintText"
+            android:id="@+id/custom_content_hint"
             android:layout_width="160dp"
             android:layout_height="wrap_content"
             android:layout_gravity="top"
             android:layout_marginStart="10dp"
             android:layout_marginTop="100dp"
+            android:visibility="gone"
+            android:drawableStart="@drawable/cling_arrow_left"
+            android:drawablePadding="10dp"
             android:text="@string/first_run_cling_custom_content_hint" />
         <TextView
             style="@style/ClingHintText"
@@ -70,6 +78,7 @@
             android:layout_marginEnd="10dp"
             android:layout_marginBottom="100dp"
             android:drawableEnd="@drawable/cling_arrow_right"
+            android:drawablePadding="5dp"
             android:text="@string/first_run_cling_create_screens_hint" />
     </FrameLayout>
     <Button
diff --git a/res/layout/apps_customize_pane.xml b/res/layout/apps_customize_pane.xml
index b01add9..e488601 100644
--- a/res/layout/apps_customize_pane.xml
+++ b/res/layout/apps_customize_pane.xml
@@ -16,7 +16,7 @@
 <com.android.launcher3.AppsCustomizeTabHost
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
-    android:background="#FF000000">
+    android:background="#80FFFFFF">
     <LinearLayout
         android:id="@+id/apps_customize_content"
         android:orientation="vertical"
diff --git a/res/layout/apps_customize_widget.xml b/res/layout/apps_customize_widget.xml
index ad677e9..f2d2342 100644
--- a/res/layout/apps_customize_widget.xml
+++ b/res/layout/apps_customize_widget.xml
@@ -35,8 +35,7 @@
         android:paddingStart="@dimen/app_widget_preview_padding_left"
         android:paddingEnd="@dimen/app_widget_preview_padding_right"
         android:scaleType="matrix"
-        android:background="@drawable/widget_container_holo" />
-
+        android:background="@drawable/screenpanel" />
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
@@ -57,7 +56,10 @@
 
             android:textColor="#FFFFFFFF"
             android:textSize="13sp"
-            android:textAlignment="viewStart" />
+            android:textAlignment="viewStart"
+            android:fontFamily="sans-serif-condensed"
+            android:shadowRadius="2.0"
+            android:shadowColor="#B0000000" />
 
         <!-- The original dimensions of the widget (can't be the same text as above due to different
              style. -->
@@ -70,8 +72,11 @@
             android:layout_weight="0"
             android:gravity="start"
 
-            android:textColor="#FF555555"
-            android:textSize="12sp" />
+            android:textColor="#FFAAAAAA"
+            android:textSize="12sp"
+            android:fontFamily="sans-serif-condensed"
+            android:shadowRadius="2.0"
+            android:shadowColor="#B0000000" />
     </LinearLayout>
 
 
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 48a06fc..43a856d 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -35,4 +35,6 @@
     <color name="wallpaper_picker_translucent_gray">#66000000</color>
     <color name="folder_items_text_color">#FF333333</color>
     <color name="outline_color">#FFFFFFFF</color>
+    
+    <color name="first_run_cling_circle_background_color">#FF83AEE8</color>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index e6bb935..9f2a105 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -94,10 +94,11 @@
     </style>
 
     <style name="WorkspaceIcon.AppsCustomize">
-        <item name="android:shadowRadius">0.0</item> <!-- Don't use text shadow -->
         <item name="android:background">@null</item>
         <item name="android:textColor">@color/apps_customize_icon_text_color</item>
         <item name="android:drawablePadding">4dp</item>
+        <item name="android:shadowRadius">4.0</item>
+        <item name="android:shadowColor">#FF000000</item>
     </style>
 
     <style name="QSBBar">
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index 213e50a..dd870e4 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -499,13 +499,7 @@
             if (mPressedIcon != null) {
                 mPressedIcon.lockDrawableState();
             }
-
-            // NOTE: We want all transitions from launcher to act as if the wallpaper were enabled
-            // to be consistent.  So re-enable the flag here, and we will re-disable it as necessary
-            // when Launcher resumes and we are still in AllApps.
-            mLauncher.updateWallpaperVisibility(true);
             mLauncher.startActivitySafely(v, appInfo.intent, appInfo);
-
         } else if (v instanceof PagedViewWidget) {
             // Let the user know that they have to long press to add a widget
             if (mWidgetInstructionToast != null) {
diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java
index 89e74b2..bfcf92a 100644
--- a/src/com/android/launcher3/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher3/AppsCustomizeTabHost.java
@@ -22,6 +22,7 @@
 import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Color;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
@@ -81,13 +82,6 @@
         setOnTabChangedListener(this);
     }
 
-    void selectAppsTab() {
-        setContentTypeImmediate(AppsCustomizePagedView.ContentType.Applications);
-    }
-    void selectWidgetsTab() {
-        setContentTypeImmediate(AppsCustomizePagedView.ContentType.Widgets);
-    }
-
     @Override
     public void setInsets(Rect insets) {
         mInsets.set(insets);
@@ -203,6 +197,9 @@
     }
 
     private void onTabChangedEnd(AppsCustomizePagedView.ContentType type) {
+        int bgAlpha = (int) (255 * (getResources().getInteger(
+            R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f));
+        setBackgroundColor(Color.argb(bgAlpha, 0, 0, 0));
         mAppsCustomizePane.setContentType(type);
     }
 
@@ -439,6 +436,7 @@
         ViewGroup parent = (ViewGroup) getParent();
         if (parent == null) return;
 
+        View overviewPanel = ((Launcher) getContext()).getOverviewPanel();
         final int count = parent.getChildCount();
         if (!isChildrenDrawingOrderEnabled()) {
             for (int i = 0; i < count; i++) {
@@ -446,7 +444,7 @@
                 if (child == this) {
                     break;
                 } else {
-                    if (child.getVisibility() == GONE) {
+                    if (child.getVisibility() == GONE || child == overviewPanel) {
                         continue;
                     }
                     child.setVisibility(visibility);
diff --git a/src/com/android/launcher3/AutoScrollHelper.java b/src/com/android/launcher3/AutoScrollHelper.java
deleted file mode 100644
index 3efd807..0000000
--- a/src/com/android/launcher3/AutoScrollHelper.java
+++ /dev/null
@@ -1,867 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.res.Resources;
-import android.os.SystemClock;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v4.view.ViewCompat;
-import android.util.DisplayMetrics;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
-
-/**
- * AutoScrollHelper is a utility class for adding automatic edge-triggered
- * scrolling to Views.
- * <p>
- * <b>Note:</b> Implementing classes are responsible for overriding the
- * {@link #scrollTargetBy}, {@link #canTargetScrollHorizontally}, and
- * {@link #canTargetScrollVertically} methods. See
- * {@link ListViewAutoScrollHelper} for a {@link android.widget.ListView}
- * -specific implementation.
- * <p>
- * <h1>Activation</h1> Automatic scrolling starts when the user touches within
- * an activation area. By default, activation areas are defined as the top,
- * left, right, and bottom 20% of the host view's total area. Touching within
- * the top activation area scrolls up, left scrolls to the left, and so on.
- * <p>
- * As the user touches closer to the extreme edge of the activation area,
- * scrolling accelerates up to a maximum velocity. When using the default edge
- * type, {@link #EDGE_TYPE_INSIDE_EXTEND}, moving outside of the view bounds
- * will scroll at the maximum velocity.
- * <p>
- * The following activation properties may be configured:
- * <ul>
- * <li>Delay after entering activation area before auto-scrolling begins, see
- * {@link #setActivationDelay}. Default value is
- * {@link ViewConfiguration#getTapTimeout()} to avoid conflicting with taps.
- * <li>Location of activation areas, see {@link #setEdgeType}. Default value is
- * {@link #EDGE_TYPE_INSIDE_EXTEND}.
- * <li>Size of activation areas relative to view size, see
- * {@link #setRelativeEdges}. Default value is 20% for both vertical and
- * horizontal edges.
- * <li>Maximum size used to constrain relative size, see
- * {@link #setMaximumEdges}. Default value is {@link #NO_MAX}.
- * </ul>
- * <h1>Scrolling</h1> When automatic scrolling is active, the helper will
- * repeatedly call {@link #scrollTargetBy} to apply new scrolling offsets.
- * <p>
- * The following scrolling properties may be configured:
- * <ul>
- * <li>Acceleration ramp-up duration, see {@link #setRampUpDuration}. Default
- * value is 2500 milliseconds.
- * <li>Acceleration ramp-down duration, see {@link #setRampDownDuration}.
- * Default value is 500 milliseconds.
- * <li>Target velocity relative to view size, see {@link #setRelativeVelocity}.
- * Default value is 100% per second for both vertical and horizontal.
- * <li>Minimum velocity used to constrain relative velocity, see
- * {@link #setMinimumVelocity}. When set, scrolling will accelerate to the
- * larger of either this value or the relative target value. Default value is
- * approximately 5 centimeters or 315 dips per second.
- * <li>Maximum velocity used to constrain relative velocity, see
- * {@link #setMaximumVelocity}. Default value is approximately 25 centimeters or
- * 1575 dips per second.
- * </ul>
- */
-public abstract class AutoScrollHelper implements View.OnTouchListener {
-    /**
-     * Constant passed to {@link #setRelativeEdges} or
-     * {@link #setRelativeVelocity}. Using this value ensures that the computed
-     * relative value is ignored and the absolute maximum value is always used.
-     */
-    public static final float RELATIVE_UNSPECIFIED = 0;
-
-    /**
-     * Constant passed to {@link #setMaximumEdges}, {@link #setMaximumVelocity},
-     * or {@link #setMinimumVelocity}. Using this value ensures that the
-     * computed relative value is always used without constraining to a
-     * particular minimum or maximum value.
-     */
-    public static final float NO_MAX = Float.MAX_VALUE;
-
-    /**
-     * Constant passed to {@link #setMaximumEdges}, or
-     * {@link #setMaximumVelocity}, or {@link #setMinimumVelocity}. Using this
-     * value ensures that the computed relative value is always used without
-     * constraining to a particular minimum or maximum value.
-     */
-    public static final float NO_MIN = 0;
-
-    /**
-     * Edge type that specifies an activation area starting at the view bounds
-     * and extending inward. Moving outside the view bounds will stop scrolling.
-     *
-     * @see #setEdgeType
-     */
-    public static final int EDGE_TYPE_INSIDE = 0;
-
-    /**
-     * Edge type that specifies an activation area starting at the view bounds
-     * and extending inward. After activation begins, moving outside the view
-     * bounds will continue scrolling.
-     *
-     * @see #setEdgeType
-     */
-    public static final int EDGE_TYPE_INSIDE_EXTEND = 1;
-
-    /**
-     * Edge type that specifies an activation area starting at the view bounds
-     * and extending outward. Moving inside the view bounds will stop scrolling.
-     *
-     * @see #setEdgeType
-     */
-    public static final int EDGE_TYPE_OUTSIDE = 2;
-
-    private static final int HORIZONTAL = 0;
-    private static final int VERTICAL = 1;
-
-    /** Scroller used to control acceleration toward maximum velocity. */
-    private final ClampedScroller mScroller = new ClampedScroller();
-
-    /** Interpolator used to scale velocity with touch position. */
-    private final Interpolator mEdgeInterpolator = new AccelerateInterpolator();
-
-    /** The view to auto-scroll. Might not be the source of touch events. */
-    private final View mTarget;
-
-    /** Runnable used to animate scrolling. */
-    private Runnable mRunnable;
-
-    /** Edge insets used to activate auto-scrolling. */
-    private float[] mRelativeEdges = new float[] { RELATIVE_UNSPECIFIED, RELATIVE_UNSPECIFIED };
-
-    /** Clamping values for edge insets used to activate auto-scrolling. */
-    private float[] mMaximumEdges = new float[] { NO_MAX, NO_MAX };
-
-    /** The type of edge being used. */
-    private int mEdgeType;
-
-    /** Delay after entering an activation edge before auto-scrolling begins. */
-    private int mActivationDelay;
-
-    /** Relative scrolling velocity at maximum edge distance. */
-    private float[] mRelativeVelocity = new float[] { RELATIVE_UNSPECIFIED, RELATIVE_UNSPECIFIED };
-
-    /** Clamping values used for scrolling velocity. */
-    private float[] mMinimumVelocity = new float[] { NO_MIN, NO_MIN };
-
-    /** Clamping values used for scrolling velocity. */
-    private float[] mMaximumVelocity = new float[] { NO_MAX, NO_MAX };
-
-    /** Whether to start activation immediately. */
-    private boolean mAlreadyDelayed;
-
-    /** Whether to reset the scroller start time on the next animation. */
-    private boolean mNeedsReset;
-
-    /** Whether to send a cancel motion event to the target view. */
-    private boolean mNeedsCancel;
-
-    /** Whether the auto-scroller is actively scrolling. */
-    private boolean mAnimating;
-
-    /** Whether the auto-scroller is enabled. */
-    private boolean mEnabled;
-
-    /** Whether the auto-scroller consumes events when scrolling. */
-    private boolean mExclusive;
-
-    // Default values.
-    private static final int DEFAULT_EDGE_TYPE = EDGE_TYPE_INSIDE_EXTEND;
-    private static final int DEFAULT_MINIMUM_VELOCITY_DIPS = 315;
-    private static final int DEFAULT_MAXIMUM_VELOCITY_DIPS = 1575;
-    private static final float DEFAULT_MAXIMUM_EDGE = NO_MAX;
-    private static final float DEFAULT_RELATIVE_EDGE = 0.2f;
-    private static final float DEFAULT_RELATIVE_VELOCITY = 1f;
-    private static final int DEFAULT_ACTIVATION_DELAY = ViewConfiguration.getTapTimeout();
-    private static final int DEFAULT_RAMP_UP_DURATION = 500;
-    private static final int DEFAULT_RAMP_DOWN_DURATION = 500;
-
-    /**
-     * Creates a new helper for scrolling the specified target view.
-     * <p>
-     * The resulting helper may be configured by chaining setter calls and
-     * should be set as a touch listener on the target view.
-     * <p>
-     * By default, the helper is disabled and will not respond to touch events
-     * until it is enabled using {@link #setEnabled}.
-     *
-     * @param target The view to automatically scroll.
-     */
-    public AutoScrollHelper(View target) {
-        mTarget = target;
-
-        final DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
-        final int maxVelocity = (int) (DEFAULT_MAXIMUM_VELOCITY_DIPS * metrics.density + 0.5f);
-        final int minVelocity = (int) (DEFAULT_MINIMUM_VELOCITY_DIPS * metrics.density + 0.5f);
-        setMaximumVelocity(maxVelocity, maxVelocity);
-        setMinimumVelocity(minVelocity, minVelocity);
-
-        setEdgeType(DEFAULT_EDGE_TYPE);
-        setMaximumEdges(DEFAULT_MAXIMUM_EDGE, DEFAULT_MAXIMUM_EDGE);
-        setRelativeEdges(DEFAULT_RELATIVE_EDGE, DEFAULT_RELATIVE_EDGE);
-        setRelativeVelocity(DEFAULT_RELATIVE_VELOCITY, DEFAULT_RELATIVE_VELOCITY);
-        setActivationDelay(DEFAULT_ACTIVATION_DELAY);
-        setRampUpDuration(DEFAULT_RAMP_UP_DURATION);
-        setRampDownDuration(DEFAULT_RAMP_DOWN_DURATION);
-    }
-
-    /**
-     * Sets whether the scroll helper is enabled and should respond to touch
-     * events.
-     *
-     * @param enabled Whether the scroll helper is enabled.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    public AutoScrollHelper setEnabled(boolean enabled) {
-        if (mEnabled && !enabled) {
-            requestStop();
-        }
-
-        mEnabled = enabled;
-        return this;
-    }
-
-    /**
-     * @return True if this helper is enabled and responding to touch events.
-     */
-    public boolean isEnabled() {
-        return mEnabled;
-    }
-
-    /**
-     * Enables or disables exclusive handling of touch events during scrolling.
-     * By default, exclusive handling is disabled and the target view receives
-     * all touch events.
-     * <p>
-     * When enabled, {@link #onTouch} will return true if the helper is
-     * currently scrolling and false otherwise.
-     *
-     * @param exclusive True to exclusively handle touch events during scrolling,
-     *            false to allow the target view to receive all touch events.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    public AutoScrollHelper setExclusive(boolean exclusive) {
-        mExclusive = exclusive;
-        return this;
-    }
-
-    /**
-     * Indicates whether the scroll helper handles touch events exclusively
-     * during scrolling.
-     *
-     * @return True if exclusive handling of touch events during scrolling is
-     *         enabled, false otherwise.
-     * @see #setExclusive(boolean)
-     */
-    public boolean isExclusive() {
-        return mExclusive;
-    }
-
-    /**
-     * Sets the absolute maximum scrolling velocity.
-     * <p>
-     * If relative velocity is not specified, scrolling will always reach the
-     * same maximum velocity. If both relative and maximum velocities are
-     * specified, the maximum velocity will be used to clamp the calculated
-     * relative velocity.
-     *
-     * @param horizontalMax The maximum horizontal scrolling velocity, or
-     *            {@link #NO_MAX} to leave the relative value unconstrained.
-     * @param verticalMax The maximum vertical scrolling velocity, or
-     *            {@link #NO_MAX} to leave the relative value unconstrained.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    public AutoScrollHelper setMaximumVelocity(float horizontalMax, float verticalMax) {
-        mMaximumVelocity[HORIZONTAL] = horizontalMax / 1000f;
-        mMaximumVelocity[VERTICAL] = verticalMax / 1000f;
-        return this;
-    }
-
-    /**
-     * Sets the absolute minimum scrolling velocity.
-     * <p>
-     * If both relative and minimum velocities are specified, the minimum
-     * velocity will be used to clamp the calculated relative velocity.
-     *
-     * @param horizontalMin The minimum horizontal scrolling velocity, or
-     *            {@link #NO_MIN} to leave the relative value unconstrained.
-     * @param verticalMin The minimum vertical scrolling velocity, or
-     *            {@link #NO_MIN} to leave the relative value unconstrained.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    public AutoScrollHelper setMinimumVelocity(float horizontalMin, float verticalMin) {
-        mMinimumVelocity[HORIZONTAL] = horizontalMin / 1000f;
-        mMinimumVelocity[VERTICAL] = verticalMin / 1000f;
-        return this;
-    }
-
-    /**
-     * Sets the target scrolling velocity relative to the host view's
-     * dimensions.
-     * <p>
-     * If both relative and maximum velocities are specified, the maximum
-     * velocity will be used to clamp the calculated relative velocity.
-     *
-     * @param horizontal The target horizontal velocity as a fraction of the
-     *            host view width per second, or {@link #RELATIVE_UNSPECIFIED}
-     *            to ignore.
-     * @param vertical The target vertical velocity as a fraction of the host
-     *            view height per second, or {@link #RELATIVE_UNSPECIFIED} to
-     *            ignore.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    public AutoScrollHelper setRelativeVelocity(float horizontal, float vertical) {
-        mRelativeVelocity[HORIZONTAL] = horizontal / 1000f;
-        mRelativeVelocity[VERTICAL] = vertical / 1000f;
-        return this;
-    }
-
-    /**
-     * Sets the activation edge type, one of:
-     * <ul>
-     * <li>{@link #EDGE_TYPE_INSIDE} for edges that respond to touches inside
-     * the bounds of the host view. If touch moves outside the bounds, scrolling
-     * will stop.
-     * <li>{@link #EDGE_TYPE_INSIDE_EXTEND} for inside edges that continued to
-     * scroll when touch moves outside the bounds of the host view.
-     * <li>{@link #EDGE_TYPE_OUTSIDE} for edges that only respond to touches
-     * that move outside the bounds of the host view.
-     * </ul>
-     *
-     * @param type The type of edge to use.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    public AutoScrollHelper setEdgeType(int type) {
-        mEdgeType = type;
-        return this;
-    }
-
-    /**
-     * Sets the activation edge size relative to the host view's dimensions.
-     * <p>
-     * If both relative and maximum edges are specified, the maximum edge will
-     * be used to constrain the calculated relative edge size.
-     *
-     * @param horizontal The horizontal edge size as a fraction of the host view
-     *            width, or {@link #RELATIVE_UNSPECIFIED} to always use the
-     *            maximum value.
-     * @param vertical The vertical edge size as a fraction of the host view
-     *            height, or {@link #RELATIVE_UNSPECIFIED} to always use the
-     *            maximum value.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    public AutoScrollHelper setRelativeEdges(float horizontal, float vertical) {
-        mRelativeEdges[HORIZONTAL] = horizontal;
-        mRelativeEdges[VERTICAL] = vertical;
-        return this;
-    }
-
-    /**
-     * Sets the absolute maximum edge size.
-     * <p>
-     * If relative edge size is not specified, activation edges will always be
-     * the maximum edge size. If both relative and maximum edges are specified,
-     * the maximum edge will be used to constrain the calculated relative edge
-     * size.
-     *
-     * @param horizontalMax The maximum horizontal edge size in pixels, or
-     *            {@link #NO_MAX} to use the unconstrained calculated relative
-     *            value.
-     * @param verticalMax The maximum vertical edge size in pixels, or
-     *            {@link #NO_MAX} to use the unconstrained calculated relative
-     *            value.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    public AutoScrollHelper setMaximumEdges(float horizontalMax, float verticalMax) {
-        mMaximumEdges[HORIZONTAL] = horizontalMax;
-        mMaximumEdges[VERTICAL] = verticalMax;
-        return this;
-    }
-
-    /**
-     * Sets the delay after entering an activation edge before activation of
-     * auto-scrolling. By default, the activation delay is set to
-     * {@link ViewConfiguration#getTapTimeout()}.
-     * <p>
-     * Specifying a delay of zero will start auto-scrolling immediately after
-     * the touch position enters an activation edge.
-     *
-     * @param delayMillis The activation delay in milliseconds.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    public AutoScrollHelper setActivationDelay(int delayMillis) {
-        mActivationDelay = delayMillis;
-        return this;
-    }
-
-    /**
-     * Sets the amount of time after activation of auto-scrolling that is takes
-     * to reach target velocity for the current touch position.
-     * <p>
-     * Specifying a duration greater than zero prevents sudden jumps in
-     * velocity.
-     *
-     * @param durationMillis The ramp-up duration in milliseconds.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    public AutoScrollHelper setRampUpDuration(int durationMillis) {
-        mScroller.setRampUpDuration(durationMillis);
-        return this;
-    }
-
-    /**
-     * Sets the amount of time after de-activation of auto-scrolling that is
-     * takes to slow to a stop.
-     * <p>
-     * Specifying a duration greater than zero prevents sudden jumps in
-     * velocity.
-     *
-     * @param durationMillis The ramp-down duration in milliseconds.
-     * @return The scroll helper, which may used to chain setter calls.
-     */
-    public AutoScrollHelper setRampDownDuration(int durationMillis) {
-        mScroller.setRampDownDuration(durationMillis);
-        return this;
-    }
-
-    /**
-     * Handles touch events by activating automatic scrolling, adjusting scroll
-     * velocity, or stopping.
-     * <p>
-     * If {@link #isExclusive()} is false, always returns false so that
-     * the host view may handle touch events. Otherwise, returns true when
-     * automatic scrolling is active and false otherwise.
-     */
-    @Override
-    public boolean onTouch(View v, MotionEvent event) {
-        if (!mEnabled) {
-            return false;
-        }
-
-        final int action = MotionEventCompat.getActionMasked(event);
-        switch (action) {
-            case MotionEvent.ACTION_DOWN:
-                mNeedsCancel = true;
-                mAlreadyDelayed = false;
-                // $FALL-THROUGH$
-            case MotionEvent.ACTION_MOVE:
-                final float xTargetVelocity = computeTargetVelocity(
-                        HORIZONTAL, event.getX(), v.getWidth(), mTarget.getWidth());
-                final float yTargetVelocity = computeTargetVelocity(
-                        VERTICAL, event.getY(), v.getHeight(), mTarget.getHeight());
-                mScroller.setTargetVelocity(xTargetVelocity, yTargetVelocity);
-
-                // If the auto scroller was not previously active, but it should
-                // be, then update the state and start animations.
-                if (!mAnimating && shouldAnimate()) {
-                    startAnimating();
-                }
-                break;
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_CANCEL:
-                requestStop();
-                break;
-        }
-
-        return mExclusive && mAnimating;
-    }
-
-    /**
-     * @return whether the target is able to scroll in the requested direction
-     */
-    private boolean shouldAnimate() {
-        final ClampedScroller scroller = mScroller;
-        final int verticalDirection = scroller.getVerticalDirection();
-        final int horizontalDirection = scroller.getHorizontalDirection();
-
-        return verticalDirection != 0 && canTargetScrollVertically(verticalDirection)
-                || horizontalDirection != 0 && canTargetScrollHorizontally(horizontalDirection);
-    }
-
-    /**
-     * Starts the scroll animation.
-     */
-    private void startAnimating() {
-        if (mRunnable == null) {
-            mRunnable = new ScrollAnimationRunnable();
-        }
-
-        mAnimating = true;
-        mNeedsReset = true;
-
-        if (!mAlreadyDelayed && mActivationDelay > 0) {
-            ViewCompat.postOnAnimationDelayed(mTarget, mRunnable, mActivationDelay);
-        } else {
-            mRunnable.run();
-        }
-
-        // If we start animating again before the user lifts their finger, we
-        // already know it's not a tap and don't need an activation delay.
-        mAlreadyDelayed = true;
-    }
-
-    /**
-     * Requests that the scroll animation slow to a stop. If there is an
-     * activation delay, this may occur between posting the animation and
-     * actually running it.
-     */
-    private void requestStop() {
-        if (mNeedsReset) {
-            // The animation has been posted, but hasn't run yet. Manually
-            // stopping animation will prevent it from running.
-            mAnimating = false;
-        } else {
-            mScroller.requestStop();
-        }
-    }
-
-    private float computeTargetVelocity(
-            int direction, float coordinate, float srcSize, float dstSize) {
-        final float relativeEdge = mRelativeEdges[direction];
-        final float maximumEdge = mMaximumEdges[direction];
-        final float value = getEdgeValue(relativeEdge, srcSize, maximumEdge, coordinate);
-        if (value == 0) {
-            // The edge in this direction is not activated.
-            return 0;
-        }
-
-        final float relativeVelocity = mRelativeVelocity[direction];
-        final float minimumVelocity = mMinimumVelocity[direction];
-        final float maximumVelocity = mMaximumVelocity[direction];
-        final float targetVelocity = relativeVelocity * dstSize;
-
-        // Target velocity is adjusted for interpolated edge position, then
-        // clamped to the minimum and maximum values. Later, this value will be
-        // adjusted for time-based acceleration.
-        if (value > 0) {
-            return constrain(value * targetVelocity, minimumVelocity, maximumVelocity);
-        } else {
-            return -constrain(-value * targetVelocity, minimumVelocity, maximumVelocity);
-        }
-    }
-
-    /**
-     * Override this method to scroll the target view by the specified number of
-     * pixels.
-     *
-     * @param deltaX The number of pixels to scroll by horizontally.
-     * @param deltaY The number of pixels to scroll by vertically.
-     */
-    public abstract void scrollTargetBy(int deltaX, int deltaY);
-
-    /**
-     * Override this method to return whether the target view can be scrolled
-     * horizontally in a certain direction.
-     *
-     * @param direction Negative to check scrolling left, positive to check
-     *            scrolling right.
-     * @return true if the target view is able to horizontally scroll in the
-     *         specified direction.
-     */
-    public abstract boolean canTargetScrollHorizontally(int direction);
-
-    /**
-     * Override this method to return whether the target view can be scrolled
-     * vertically in a certain direction.
-     *
-     * @param direction Negative to check scrolling up, positive to check
-     *            scrolling down.
-     * @return true if the target view is able to vertically scroll in the
-     *         specified direction.
-     */
-    public abstract boolean canTargetScrollVertically(int direction);
-
-    /**
-     * Returns the interpolated position of a touch point relative to an edge
-     * defined by its relative inset, its maximum absolute inset, and the edge
-     * interpolator.
-     *
-     * @param relativeValue The size of the inset relative to the total size.
-     * @param size Total size.
-     * @param maxValue The maximum size of the inset, used to clamp (relative *
-     *            total).
-     * @param current Touch position within within the total size.
-     * @return Interpolated value of the touch position within the edge.
-     */
-    private float getEdgeValue(float relativeValue, float size, float maxValue, float current) {
-        // For now, leading and trailing edges are always the same size.
-        final float edgeSize = constrain(relativeValue * size, NO_MIN, maxValue);
-        final float valueLeading = constrainEdgeValue(current, edgeSize);
-        final float valueTrailing = constrainEdgeValue(size - current, edgeSize);
-        final float value = (valueTrailing - valueLeading);
-        final float interpolated;
-        if (value < 0) {
-            interpolated = -mEdgeInterpolator.getInterpolation(-value);
-        } else if (value > 0) {
-            interpolated = mEdgeInterpolator.getInterpolation(value);
-        } else {
-            return 0;
-        }
-
-        return constrain(interpolated, -1, 1);
-    }
-
-    private float constrainEdgeValue(float current, float leading) {
-        if (leading == 0) {
-            return 0;
-        }
-
-        switch (mEdgeType) {
-            case EDGE_TYPE_INSIDE:
-            case EDGE_TYPE_INSIDE_EXTEND:
-                if (current < leading) {
-                    if (current > 0) {
-                        // Movement up to the edge is scaled.
-                        return 1f - current / leading;
-                    } else if (mAnimating && (mEdgeType == EDGE_TYPE_INSIDE_EXTEND)) {
-                        // Movement beyond the edge is always maximum.
-                        return 1f;
-                    }
-                }
-                break;
-            case EDGE_TYPE_OUTSIDE:
-                if (current < 0) {
-                    // Movement beyond the edge is scaled.
-                    return current / -leading;
-                }
-                break;
-        }
-
-        return 0;
-    }
-
-    private static int constrain(int value, int min, int max) {
-        if (value > max) {
-            return max;
-        } else if (value < min) {
-            return min;
-        } else {
-            return value;
-        }
-    }
-
-    private static float constrain(float value, float min, float max) {
-        if (value > max) {
-            return max;
-        } else if (value < min) {
-            return min;
-        } else {
-            return value;
-        }
-    }
-
-    /**
-     * Sends a {@link MotionEvent#ACTION_CANCEL} event to the target view,
-     * canceling any ongoing touch events.
-     */
-    private void cancelTargetTouch() {
-        final long eventTime = SystemClock.uptimeMillis();
-        final MotionEvent cancel = MotionEvent.obtain(
-                eventTime, eventTime, MotionEvent.ACTION_CANCEL, 0, 0, 0);
-        mTarget.onTouchEvent(cancel);
-        cancel.recycle();
-    }
-
-    private class ScrollAnimationRunnable implements Runnable {
-        @Override
-        public void run() {
-            if (!mAnimating) {
-                return;
-            }
-
-            if (mNeedsReset) {
-                mNeedsReset = false;
-                mScroller.start();
-            }
-
-            final ClampedScroller scroller = mScroller;
-            if (scroller.isFinished() || !shouldAnimate()) {
-                mAnimating = false;
-                return;
-            }
-
-            if (mNeedsCancel) {
-                mNeedsCancel = false;
-                cancelTargetTouch();
-            }
-
-            scroller.computeScrollDelta();
-
-            final int deltaX = scroller.getDeltaX();
-            final int deltaY = scroller.getDeltaY();
-            scrollTargetBy(deltaX,  deltaY);
-
-            // Keep going until the scroller has permanently stopped.
-            ViewCompat.postOnAnimation(mTarget, this);
-        }
-    }
-
-    /**
-     * Scroller whose velocity follows the curve of an {@link Interpolator} and
-     * is clamped to the interpolated 0f value before starting and the
-     * interpolated 1f value after a specified duration.
-     */
-    private static class ClampedScroller {
-        private int mRampUpDuration;
-        private int mRampDownDuration;
-        private float mTargetVelocityX;
-        private float mTargetVelocityY;
-
-        private long mStartTime;
-
-        private long mDeltaTime;
-        private int mDeltaX;
-        private int mDeltaY;
-
-        private long mStopTime;
-        private float mStopValue;
-        private int mEffectiveRampDown;
-
-        /**
-         * Creates a new ramp-up scroller that reaches full velocity after a
-         * specified duration.
-         */
-        public ClampedScroller() {
-            mStartTime = Long.MIN_VALUE;
-            mStopTime = -1;
-            mDeltaTime = 0;
-            mDeltaX = 0;
-            mDeltaY = 0;
-        }
-
-        public void setRampUpDuration(int durationMillis) {
-            mRampUpDuration = durationMillis;
-        }
-
-        public void setRampDownDuration(int durationMillis) {
-            mRampDownDuration = durationMillis;
-        }
-
-        /**
-         * Starts the scroller at the current animation time.
-         */
-        public void start() {
-            mStartTime = AnimationUtils.currentAnimationTimeMillis();
-            mStopTime = -1;
-            mDeltaTime = mStartTime;
-            mStopValue = 0.5f;
-            mDeltaX = 0;
-            mDeltaY = 0;
-        }
-
-        /**
-         * Stops the scroller at the current animation time.
-         */
-        public void requestStop() {
-            final long currentTime = AnimationUtils.currentAnimationTimeMillis();
-            mEffectiveRampDown = constrain((int) (currentTime - mStartTime), 0, mRampDownDuration);
-            mStopValue = getValueAt(currentTime);
-            mStopTime = currentTime;
-        }
-
-        public boolean isFinished() {
-            return mStopTime > 0
-                    && AnimationUtils.currentAnimationTimeMillis() > mStopTime + mEffectiveRampDown;
-        }
-
-        private float getValueAt(long currentTime) {
-            if (currentTime < mStartTime) {
-                return 0f;
-            } else if (mStopTime < 0 || currentTime < mStopTime) {
-                final long elapsedSinceStart = currentTime - mStartTime;
-                return 0.5f * constrain(elapsedSinceStart / (float) mRampUpDuration, 0, 1);
-            } else {
-                final long elapsedSinceEnd = currentTime - mStopTime;
-                return (1 - mStopValue) + mStopValue
-                        * constrain(elapsedSinceEnd / (float) mEffectiveRampDown, 0, 1);
-            }
-        }
-
-        /**
-         * Interpolates the value along a parabolic curve corresponding to the equation
-         * <code>y = -4x * (x-1)</code>.
-         *
-         * @param value The value to interpolate, between 0 and 1.
-         * @return the interpolated value, between 0 and 1.
-         */
-        private float interpolateValue(float value) {
-            return -4 * value * value + 4 * value;
-        }
-
-        /**
-         * Computes the current scroll deltas. This usually only be called after
-         * starting the scroller with {@link #start()}.
-         *
-         * @see #getDeltaX()
-         * @see #getDeltaY()
-         */
-        public void computeScrollDelta() {
-            if (mDeltaTime == 0) {
-                throw new RuntimeException("Cannot compute scroll delta before calling start()");
-            }
-
-            final long currentTime = AnimationUtils.currentAnimationTimeMillis();
-            final float value = getValueAt(currentTime);
-            final float scale = interpolateValue(value);
-            final long elapsedSinceDelta = currentTime - mDeltaTime;
-
-            mDeltaTime = currentTime;
-            mDeltaX = (int) (elapsedSinceDelta * scale * mTargetVelocityX);
-            mDeltaY = (int) (elapsedSinceDelta * scale * mTargetVelocityY);
-        }
-
-        /**
-         * Sets the target velocity for this scroller.
-         *
-         * @param x The target X velocity in pixels per millisecond.
-         * @param y The target Y velocity in pixels per millisecond.
-         */
-        public void setTargetVelocity(float x, float y) {
-            mTargetVelocityX = x;
-            mTargetVelocityY = y;
-        }
-
-        public int getHorizontalDirection() {
-            return (int) (mTargetVelocityX / Math.abs(mTargetVelocityX));
-        }
-
-        public int getVerticalDirection() {
-            return (int) (mTargetVelocityY / Math.abs(mTargetVelocityY));
-        }
-
-        /**
-         * The distance traveled in the X-coordinate computed by the last call
-         * to {@link #computeScrollDelta()}.
-         */
-        public int getDeltaX() {
-            return mDeltaX;
-        }
-
-        /**
-         * The distance traveled in the Y-coordinate computed by the last call
-         * to {@link #computeScrollDelta()}.
-         */
-        public int getDeltaY() {
-            return mDeltaY;
-        }
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 30ca737..22492ac 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -949,9 +949,11 @@
         int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
         int widthSize = MeasureSpec.getSize(widthMeasureSpec);
         int heightSize =  MeasureSpec.getSize(heightMeasureSpec);
+        int childWidthSize = widthSize - (getPaddingLeft() + getPaddingRight());
+        int childHeightSize = heightSize - (getPaddingTop() + getPaddingBottom());
         if (mFixedCellWidth < 0 || mFixedCellHeight < 0) {
-            int cw = grid.calculateCellWidth(widthSize, mCountX);
-            int ch = grid.calculateCellHeight(heightSize, mCountY);
+            int cw = grid.calculateCellWidth(childWidthSize, mCountX);
+            int ch = grid.calculateCellHeight(childHeightSize, mCountY);
             if (cw != mCellWidth || ch != mCellHeight) {
                 mCellWidth = cw;
                 mCellHeight = ch;
@@ -960,8 +962,8 @@
             }
         }
 
-        int newWidth = widthSize;
-        int newHeight = heightSize;
+        int newWidth = childWidthSize;
+        int newHeight = childHeightSize;
         if (mFixedWidth > 0 && mFixedHeight > 0) {
             newWidth = mFixedWidth;
             newHeight = mFixedHeight;
@@ -973,8 +975,8 @@
         int numHeightGaps = mCountY - 1;
 
         if (mOriginalWidthGap < 0 || mOriginalHeightGap < 0) {
-            int hSpace = widthSize - getPaddingLeft() - getPaddingRight();
-            int vSpace = heightSize - getPaddingTop() - getPaddingBottom();
+            int hSpace = childWidthSize;
+            int vSpace = childHeightSize;
             int hFreeSpace = hSpace - (mCountX * mCellWidth);
             int vFreeSpace = vSpace - (mCountY * mCellHeight);
             mWidthGap = Math.min(mMaxGap, numWidthGaps > 0 ? (hFreeSpace / numWidthGaps) : 0);
@@ -990,15 +992,19 @@
         int maxHeight = 0;
         for (int i = 0; i < count; i++) {
             View child = getChildAt(i);
-            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(newWidth - getPaddingLeft() -
-                    getPaddingRight(), MeasureSpec.EXACTLY);
-            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight - getPaddingTop() -
-                    getPaddingBottom(), MeasureSpec.EXACTLY);
+            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(newWidth,
+                    MeasureSpec.EXACTLY);
+            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight,
+                    MeasureSpec.EXACTLY);
             child.measure(childWidthMeasureSpec, childheightMeasureSpec);
             maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
             maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
         }
-        setMeasuredDimension(maxWidth, maxHeight);
+        if (mFixedWidth > 0 && mFixedHeight > 0) {
+            setMeasuredDimension(maxWidth, maxHeight);
+        } else {
+            setMeasuredDimension(widthSize, heightSize);
+        }
     }
 
     @Override
@@ -1006,8 +1012,11 @@
         int count = getChildCount();
         for (int i = 0; i < count; i++) {
             View child = getChildAt(i);
-            child.layout(getPaddingLeft(), getPaddingTop(),
-                    r - l - getPaddingRight(), b - t - getPaddingBottom());
+            int left = getPaddingLeft();
+            int top = getPaddingTop();
+            child.layout(left, top,
+                    left + r - l,
+                    top + b - t);
         }
     }
 
diff --git a/src/com/android/launcher3/Cling.java b/src/com/android/launcher3/Cling.java
index 963702a..7ca6990 100644
--- a/src/com/android/launcher3/Cling.java
+++ b/src/com/android/launcher3/Cling.java
@@ -24,6 +24,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
@@ -55,7 +56,7 @@
     private static String FOLDER_LANDSCAPE = "folder_landscape";
     private static String FOLDER_LARGE = "folder_large";
 
-    private static float FIRST_RUN_CIRCLE_BUFFER_DPS = 40;
+    private static float FIRST_RUN_CIRCLE_BUFFER_DPS = 60;
     private static float WORKSPACE_INNER_CIRCLE_RADIUS_DPS = 50;
     private static float WORKSPACE_OUTER_CIRCLE_RADIUS_DPS = 60;
     private static float WORKSPACE_CIRCLE_Y_OFFSET_DPS = 30;
@@ -101,8 +102,10 @@
             mErasePaint.setAlpha(0);
             mErasePaint.setAntiAlias(true);
 
+            int circleColor = getResources().getColor(
+                    R.color.first_run_cling_circle_background_color);
             mBubblePaint = new Paint();
-            mBubblePaint.setColor(0xFFFFFF);
+            mBubblePaint.setColor(circleColor);
             mBubblePaint.setAntiAlias(true);
 
             mDotPaint = new Paint();
diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java
index 664a99c..495e930 100644
--- a/src/com/android/launcher3/DynamicGrid.java
+++ b/src/com/android/launcher3/DynamicGrid.java
@@ -290,8 +290,7 @@
 
     Rect getWorkspacePadding(int orientation) {
         Rect padding = new Rect();
-        if (orientation == CellLayout.LANDSCAPE &&
-                transposeLayoutWithOrientation) {
+        if (isVerticalBarLayout()) {
             // Pad the left and right of the workspace with search/hotseat bar sizes
             padding.set(searchBarSpaceHeightPx, edgeMarginPx,
                     hotseatBarHeightPx, edgeMarginPx);
@@ -321,6 +320,17 @@
         return padding;
     }
 
+    // The rect returned will be extended to below the system ui that covers the workspace
+    Rect getHotseatRect() {
+        if (isVerticalBarLayout()) {
+            return new Rect(availableWidthPx - hotseatBarHeightPx, 0,
+                    Integer.MAX_VALUE, availableHeightPx);
+        } else {
+            return new Rect(0, availableHeightPx - hotseatBarHeightPx,
+                    availableWidthPx, Integer.MAX_VALUE);
+        }
+    }
+
     int calculateCellWidth(int width, int countX) {
         return width / countX;
     }
@@ -338,11 +348,14 @@
         return isLargeTablet;
     }
 
+    boolean isVerticalBarLayout() {
+        return isLandscape && transposeLayoutWithOrientation;
+    }
+
     public void layout(Launcher launcher) {
         FrameLayout.LayoutParams lp;
         Resources res = launcher.getResources();
-        boolean hasVerticalBarLayout = isLandscape &&
-                res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
+        boolean hasVerticalBarLayout = isVerticalBarLayout();
 
         // Layout the search bar space
         View searchBarSpace = launcher.findViewById(R.id.qsb_bar);
@@ -430,7 +443,7 @@
             lp.gravity = Gravity.BOTTOM;
             lp.width = LayoutParams.MATCH_PARENT;
             lp.height = hotseatBarHeightPx;
-            hotseat.setPadding(2 * edgeMarginPx, 0,
+            hotseat.findViewById(R.id.layout).setPadding(2 * edgeMarginPx, 0,
                     2 * edgeMarginPx, 0);
         }
         hotseat.setLayoutParams(lp);
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 9ee3f64..a71d9b2 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -26,6 +26,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.SystemClock;
+import android.support.v4.widget.AutoScrollHelper;
 import android.text.InputType;
 import android.text.Selection;
 import android.text.Spannable;
diff --git a/src/com/android/launcher3/FolderAutoScrollHelper.java b/src/com/android/launcher3/FolderAutoScrollHelper.java
index 68edc60..40e8884 100644
--- a/src/com/android/launcher3/FolderAutoScrollHelper.java
+++ b/src/com/android/launcher3/FolderAutoScrollHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import android.support.v4.widget.AutoScrollHelper;
 import android.widget.ScrollView;
 
 /**
@@ -36,6 +37,8 @@
         setEdgeType(EDGE_TYPE_INSIDE_EXTEND);
         setExclusive(true);
         setMaximumVelocity(MAX_SCROLL_VELOCITY, MAX_SCROLL_VELOCITY);
+        setRampDownDuration(0);
+        setRampUpDuration(0);
     }
 
     @Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 3bbb39e..84d5a09 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -274,6 +274,7 @@
     private boolean mVisible = false;
     private boolean mAttached = false;
     private static final boolean DISABLE_CLINGS = true;
+    private static final boolean DISABLE_CUSTOM_CLINGS = true;
 
     private static LocaleConfiguration sLocaleConfiguration = null;
 
@@ -895,11 +896,6 @@
 
     @Override
     protected void onPause() {
-        // NOTE: We want all transitions from launcher to act as if the wallpaper were enabled
-        // to be consistent.  So re-enable the flag here, and we will re-disable it as necessary
-        // when Launcher resumes and we are still in AllApps.
-        updateWallpaperVisibility(true);
-
         // Ensure that items added to Launcher are queued until Launcher returns
         InstallShortcutReceiver.enableInstallQueue();
 
@@ -2646,16 +2642,6 @@
         view.setPivotY(view.getHeight() / 2.0f);
     }
 
-    void disableWallpaperIfInAllApps() {
-        // Only disable it if we are in all apps
-        if (isAllAppsVisible()) {
-            if (mAppsCustomizeTabHost != null &&
-                    !mAppsCustomizeTabHost.isTransitioning()) {
-                updateWallpaperVisibility(false);
-            }
-        }
-    }
-
     private void setWorkspaceBackground(boolean workspace) {
         mLauncherView.setBackground(workspace ?
                 mWorkspaceBackgroundDrawable : null);
@@ -2815,7 +2801,6 @@
 
                 @Override
                 public void onAnimationStart(Animator animation) {
-                    updateWallpaperVisibility(true);
                     // Prepare the position
                     toView.setTranslationX(0.0f);
                     toView.setTranslationY(0.0f);
@@ -2827,10 +2812,6 @@
                     dispatchOnLauncherTransitionEnd(fromView, animated, false);
                     dispatchOnLauncherTransitionEnd(toView, animated, false);
 
-                    if (!animationCancelled) {
-                        updateWallpaperVisibility(false);
-                    }
-
                     // Hide the search bar
                     if (mSearchDropTargetBar != null) {
                         mSearchDropTargetBar.hideSearchBar(false);
@@ -2904,7 +2885,6 @@
             dispatchOnLauncherTransitionPrepare(toView, animated, false);
             dispatchOnLauncherTransitionStart(toView, animated, false);
             dispatchOnLauncherTransitionEnd(toView, animated, false);
-            updateWallpaperVisibility(false);
         }
     }
 
@@ -2941,7 +2921,6 @@
         }
 
         setPivotsForZoom(fromView, scaleFactor);
-        updateWallpaperVisibility(true);
         showHotseat(animated);
         if (animated) {
             final LauncherViewPropertyAnimator scaleAnim =
@@ -2973,7 +2952,6 @@
             mStateAnimation.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    updateWallpaperVisibility(true);
                     fromView.setVisibility(View.GONE);
                     dispatchOnLauncherTransitionEnd(fromView, animated, true);
                     dispatchOnLauncherTransitionEnd(toView, animated, true);
@@ -3011,30 +2989,13 @@
         }
     }
 
-    @Override
-    public void onWindowFocusChanged(boolean hasFocus) {
-        if (!hasFocus) {
-            // When another window occludes launcher (like the notification shade, or recents),
-            // ensure that we enable the wallpaper flag so that transitions are done correctly.
-            updateWallpaperVisibility(true);
-        } else {
-            // When launcher has focus again, disable the wallpaper if we are in AllApps
-            mWorkspace.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    disableWallpaperIfInAllApps();
-                }
-            }, 500);
-        }
-    }
-
     void showWorkspace(boolean animated) {
         showWorkspace(animated, null);
     }
 
     void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
         if (mState != State.WORKSPACE) {
-            boolean wasInSpringLoadedMode = (mState == State.APPS_CUSTOMIZE_SPRING_LOADED);
+            boolean wasInSpringLoadedMode = (mState != State.WORKSPACE);
             mWorkspace.setVisibility(View.VISIBLE);
             hideAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable);
 
@@ -3863,6 +3824,13 @@
         });
     }
 
+    public boolean isAllAppsButtonRank(int rank) {
+        if (mHotseat != null) {
+            return mHotseat.isAllAppsButtonRank(rank);
+        }
+        return false;
+    }
+
     private boolean canRunNewAppsAnimation() {
         long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime();
         return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
@@ -4166,16 +4134,33 @@
             // If we're not using the default workspace layout, replace workspace cling
             // with a custom workspace cling (usually specified in an overlay)
             // For now, only do this on tablets
-            if (mSharedPrefs.getInt(LauncherProvider.DEFAULT_WORKSPACE_RESOURCE_ID, 0) != 0 &&
-                    getResources().getBoolean(R.bool.config_useCustomClings)) {
-                // Use a custom cling
-                View cling = findViewById(R.id.workspace_cling);
-                ViewGroup clingParent = (ViewGroup) cling.getParent();
-                int clingIndex = clingParent.indexOfChild(cling);
-                clingParent.removeViewAt(clingIndex);
-                View customCling = mInflater.inflate(R.layout.custom_workspace_cling, clingParent, false);
-                clingParent.addView(customCling, clingIndex);
-                customCling.setId(R.id.workspace_cling);
+            if (!DISABLE_CUSTOM_CLINGS) {
+                if (mSharedPrefs.getInt(LauncherProvider.DEFAULT_WORKSPACE_RESOURCE_ID, 0) != 0 &&
+                        getResources().getBoolean(R.bool.config_useCustomClings)) {
+                    // Use a custom cling
+                    View cling = findViewById(R.id.workspace_cling);
+                    ViewGroup clingParent = (ViewGroup) cling.getParent();
+                    int clingIndex = clingParent.indexOfChild(cling);
+                    clingParent.removeViewAt(clingIndex);
+                    View customCling = mInflater.inflate(R.layout.custom_workspace_cling, clingParent, false);
+                    clingParent.addView(customCling, clingIndex);
+                    customCling.setId(R.id.workspace_cling);
+                }
+            }
+            Cling cling = (Cling) findViewById(R.id.first_run_cling);
+            if (cling != null) {
+                String sbHintStr = getFirstRunClingSearchBarHint();
+                String ccHintStr = getFirstRunCustomContentHint();
+                if (!sbHintStr.isEmpty()) {
+                    TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
+                    sbHint.setText(sbHintStr);
+                    sbHint.setVisibility(View.VISIBLE);
+                }
+                if (!ccHintStr.isEmpty()) {
+                    TextView ccHint = (TextView) cling.findViewById(R.id.custom_content_hint);
+                    ccHint.setText(ccHintStr);
+                    ccHint.setVisibility(View.VISIBLE);
+                }
             }
             initCling(R.id.first_run_cling, null, false, true);
         } else {
@@ -4183,6 +4168,13 @@
         }
     }
 
+    protected String getFirstRunClingSearchBarHint() {
+        return "";
+    }
+    protected String getFirstRunCustomContentHint() {
+        return "";
+    }
+
     public void showFirstRunWorkspaceCling() {
         // Enable the clings only if they have not been dismissed before
         if (isClingsEnabled() &&
diff --git a/src/com/android/launcher3/LauncherBackupAgent.java b/src/com/android/launcher3/LauncherBackupAgent.java
new file mode 100644
index 0000000..bb15ca1
--- /dev/null
+++ b/src/com/android/launcher3/LauncherBackupAgent.java
@@ -0,0 +1,655 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+import com.google.protobuf.nano.MessageNano;
+
+import com.android.launcher3.LauncherSettings.ChangeLogColumns;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherSettings.WorkspaceScreens;
+import com.android.launcher3.backup.BackupProtos;
+import com.android.launcher3.backup.BackupProtos.CheckedMessage;
+import com.android.launcher3.backup.BackupProtos.Favorite;
+import com.android.launcher3.backup.BackupProtos.Journal;
+import com.android.launcher3.backup.BackupProtos.Key;
+import com.android.launcher3.backup.BackupProtos.Screen;
+
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.Cursor;
+import android.os.ParcelFileDescriptor;
+import android.provider.BaseColumns;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.zip.CRC32;
+
+/**
+ * Persist the launcher home state across calamities.
+ */
+public class LauncherBackupAgent extends BackupAgent {
+
+    private static final String TAG = "LauncherBackupAgent";
+    private static final boolean DEBUG = false;
+
+    private static final int MAX_JOURNAL_SIZE = 1000000;
+
+    private static BackupManager sBackupManager;
+
+    private static final String[] FAVORITE_PROJECTION = {
+            Favorites._ID,                     // 0
+            Favorites.APPWIDGET_ID,            // 1
+            Favorites.APPWIDGET_PROVIDER,      // 2
+            Favorites.CELLX,                   // 3
+            Favorites.CELLY,                   // 4
+            Favorites.CONTAINER,               // 5
+            Favorites.ICON,                    // 6
+            Favorites.ICON_PACKAGE,            // 7
+            Favorites.ICON_RESOURCE,           // 8
+            Favorites.ICON_TYPE,               // 9
+            Favorites.ITEM_TYPE,               // 10
+            Favorites.INTENT,                  // 11
+            Favorites.SCREEN,                  // 12
+            Favorites.SPANX,                   // 13
+            Favorites.SPANY,                   // 14
+            Favorites.TITLE,                   // 15
+    };
+
+    private static final int ID_INDEX = 0;
+    private static final int APPWIDGET_ID_INDEX = 1;
+    private static final int APPWIDGET_PROVIDER_INDEX = 2;
+    private static final int CELLX_INDEX = 3;
+    private static final int CELLY_INDEX = 4;
+    private static final int CONTAINER_INDEX = 5;
+    private static final int ICON_INDEX = 6;
+    private static final int ICON_PACKAGE_INDEX = 7;
+    private static final int ICON_RESOURCE_INDEX = 8;
+    private static final int ICON_TYPE_INDEX = 9;
+    private static final int ITEM_TYPE_INDEX = 10;
+    private static final int INTENT_INDEX = 11;
+    private static final int SCREEN_INDEX = 12;
+    private static final int SPANX_INDEX = 13 ;
+    private static final int SPANY_INDEX = 14;
+    private static final int TITLE_INDEX = 15;
+
+    private static final String[] SCREEN_PROJECTION = {
+            WorkspaceScreens._ID,              // 0
+            WorkspaceScreens.SCREEN_RANK       // 1
+    };
+
+    private static final int SCREEN_RANK_INDEX = 1;
+
+    private static final String[] ID_ONLY_PROJECTION = {
+            BaseColumns._ID
+    };
+
+
+    /**
+     * Notify the backup manager that out database is dirty.
+     *
+     * <P>This does not force an immediate backup.
+     *
+     * @param context application context
+     */
+    public static void dataChanged(Context context) {
+        if (sBackupManager == null) {
+            sBackupManager = new BackupManager(context);
+        }
+        sBackupManager.dataChanged();
+    }
+
+    /**
+     * Back up launcher data so we can restore the user's state on a new device.
+     *
+     * <P>The journal is a timestamp and a list of keys that were saved as of that time.
+     *
+     * <P>Keys may come back in any order, so each key/value is one complete row of the database.
+     *
+     * @param oldState notes from the last backup
+     * @param data incremental key/value pairs to persist off-device
+     * @param newState notes for the next backup
+     * @throws IOException
+     */
+    @Override
+    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+            ParcelFileDescriptor newState)
+            throws IOException {
+        Log.v(TAG, "onBackup");
+
+        Journal in = readJournal(oldState);
+        Journal out = new Journal();
+
+        long lastBackupTime = in.t;
+        out.t = System.currentTimeMillis();
+        out.rows = 0;
+        out.bytes = 0;
+
+        Log.v(TAG, "lastBackupTime=" + lastBackupTime);
+
+        ArrayList<Key> keys = new ArrayList<Key>();
+        backupFavorites(in, data, out, keys);
+        backupScreens(in, data, out, keys);
+
+        out.key = keys.toArray(BackupProtos.Key.EMPTY_ARRAY);
+        writeJournal(newState, out);
+        Log.v(TAG, "onBackup: wrote " + out.bytes + "b in " + out.rows + " rows.");
+
+        Log.v(TAG, "onBackup: finished");
+    }
+
+    /**
+     * Restore home screen from the restored data stream.
+     *
+     * <P>Keys may arrive in any order.
+     *
+     * @param data the key/value pairs from the server
+     * @param versionCode the version of the app that generated the data
+     * @param newState notes for the next backup
+     * @throws IOException
+     */
+    @Override
+    public void onRestore(BackupDataInput data, int versionCode, ParcelFileDescriptor newState)
+            throws IOException {
+        Log.v(TAG, "onRestore");
+        int numRows = 0;
+        Journal out = new Journal();
+
+        ArrayList<Key> keys = new ArrayList<Key>();
+        byte[] buffer = new byte[512];
+        while (data.readNextHeader()) {
+            numRows++;
+            String backupKey = data.getKey();
+            int dataSize = data.getDataSize();
+            if (buffer.length < dataSize) {
+                buffer = new byte[dataSize];
+            }
+            Key key = null;
+            int bytesRead = data.readEntityData(buffer, 0, dataSize);
+            if (DEBUG) {
+                Log.d(TAG, "read " + bytesRead + " of " + dataSize + " available");
+            }
+            try {
+                key = backupKeyToKey(backupKey);
+                switch (key.type) {
+                    case Key.FAVORITE:
+                        restoreFavorite(key, buffer, dataSize, keys);
+                        break;
+
+                    case Key.SCREEN:
+                        restoreScreen(key, buffer, dataSize, keys);
+                        break;
+
+                    default:
+                        Log.w(TAG, "unknown restore entity type: " + key.type);
+                        break;
+                }
+            } catch (KeyParsingException e) {
+                Log.w(TAG, "ignoring unparsable backup key: " + backupKey);
+            }
+        }
+
+        // clear the output journal time, to force a full backup to
+        // will catch any changes the restore process might have made
+        out.t = 0;
+        out.key = keys.toArray(BackupProtos.Key.EMPTY_ARRAY);
+        writeJournal(newState, out);
+        Log.v(TAG, "onRestore: read " + numRows + " rows");
+    }
+
+    /**
+     * Write all modified favorites to the data stream.
+     *
+     *
+     * @param in notes from last backup
+     * @param data output stream for key/value pairs
+     * @param out notes about this backup
+     * @param keys keys to mark as clean in the notes for next backup
+     * @throws IOException
+     */
+    private void backupFavorites(Journal in, BackupDataOutput data, Journal out,
+            ArrayList<Key> keys)
+            throws IOException {
+        // read the old ID set
+        Set<String> savedIds = new HashSet<String>();
+        for(int i = 0; i < in.key.length; i++) {
+            Key key = in.key[i];
+            if (key.type == Key.FAVORITE) {
+                savedIds.add(keyToBackupKey(key));
+            }
+        }
+        if (DEBUG) Log.d(TAG, "favorite savedIds.size()=" + savedIds.size());
+
+        // persist things that have changed since the last backup
+        ContentResolver cr = getContentResolver();
+        String where = ChangeLogColumns.MODIFIED + " > ?";
+        String[] args = {Long.toString(in.t)};
+        String updateOrder = ChangeLogColumns.MODIFIED;
+        Cursor updated = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
+                where, args, updateOrder);
+        if (DEBUG) Log.d(TAG, "favorite updated.getCount()=" + updated.getCount());
+        try {
+            updated.moveToPosition(-1);
+            while(updated.moveToNext()) {
+                final long id = updated.getLong(ID_INDEX);
+                Key key = getKey(Key.FAVORITE, id);
+                byte[] blob = packFavorite(updated);
+                String backupKey = keyToBackupKey(key);
+                data.writeEntityHeader(backupKey, blob.length);
+                data.writeEntityData(blob, blob.length);
+                out.rows++;
+                out.bytes += blob.length;
+                Log.v(TAG, "saving favorite " + backupKey + ": " + id + "/" + blob.length);
+                if(DEBUG) Log.d(TAG, "wrote " +
+                        Base64.encodeToString(blob, 0, blob.length, Base64.NO_WRAP));
+                // remember that is was a new column, so we don't delete it.
+                savedIds.add(backupKey);
+            }
+        } finally {
+            updated.close();
+        }
+        if (DEBUG) Log.d(TAG, "favorite savedIds.size()=" + savedIds.size());
+
+        // build the current ID set
+        String idOrder = BaseColumns._ID;
+        Cursor idCursor = cr.query(Favorites.CONTENT_URI, ID_ONLY_PROJECTION,
+                null, null, idOrder);
+        Set<String> currentIds = new HashSet<String>(idCursor.getCount());
+        try {
+            idCursor.moveToPosition(-1);
+            while(idCursor.moveToNext()) {
+                Key key = getKey(Key.FAVORITE, idCursor.getLong(ID_INDEX));
+                currentIds.add(keyToBackupKey(key));
+                // save the IDs for next time
+                keys.add(key);
+            }
+        } finally {
+            idCursor.close();
+        }
+        if (DEBUG) Log.d(TAG, "favorite currentIds.size()=" + currentIds.size());
+
+        // these IDs must have been deleted
+        savedIds.removeAll(currentIds);
+        for (String deleted : savedIds) {
+            Log.v(TAG, "dropping favorite " + deleted);
+            data.writeEntityHeader(deleted, -1);
+            out.rows++;
+        }
+    }
+
+    /**
+     * Read a favorite from the stream.
+     *
+     * <P>Keys arrive in any order, so screens and containers may not exist yet.
+     *
+     * @param key identifier for the row
+     * @param buffer the serialized proto from the stream, may be larger than dataSize
+     * @param dataSize the size of the proto from the stream
+     * @param keys keys to mark as clean in the notes for next backup
+     */
+    private void restoreFavorite(Key key, byte[] buffer, int dataSize, ArrayList<Key> keys) {
+        Log.v(TAG, "unpacking favorite " + key.id + " (" + dataSize + " bytes)");
+        if (DEBUG) Log.d(TAG, "read (" + buffer.length + "): " +
+                Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP));
+
+        try {
+            Favorite favorite =  unpackFavorite(buffer, 0, dataSize);
+            if (DEBUG) Log.d(TAG, "unpacked " + favorite.itemType);
+        } catch (InvalidProtocolBufferNanoException e) {
+            Log.w(TAG, "failed to decode proto", e);
+        }
+    }
+
+    /**
+     * Write all modified screens to the data stream.
+     *
+     *
+     * @param in notes from last backup
+     * @param data output stream for key/value pairs
+     * @param out notes about this backup
+     * @param keys keys to mark as clean in the notes for next backup  @throws IOException
+     */
+    private void backupScreens(Journal in, BackupDataOutput data, Journal out,
+            ArrayList<Key> keys)
+            throws IOException {
+        // read the old ID set
+        Set<String> savedIds = new HashSet<String>();
+        for(int i = 0; i < in.key.length; i++) {
+            Key key = in.key[i];
+            if (key.type == Key.SCREEN) {
+                savedIds.add(keyToBackupKey(key));
+            }
+        }
+        if (DEBUG) Log.d(TAG, "screens savedIds.size()=" + savedIds.size());
+
+        // persist things that have changed since the last backup
+        ContentResolver cr = getContentResolver();
+        String where = ChangeLogColumns.MODIFIED + " > ?";
+        String[] args = {Long.toString(in.t)};
+        String updateOrder = ChangeLogColumns.MODIFIED;
+        Cursor updated = cr.query(WorkspaceScreens.CONTENT_URI, SCREEN_PROJECTION,
+                where, args, updateOrder);
+        updated.moveToPosition(-1);
+        if (DEBUG) Log.d(TAG, "screens updated.getCount()=" + updated.getCount());
+        try {
+            while(updated.moveToNext()) {
+                final long id = updated.getLong(ID_INDEX);
+                Key key = getKey(Key.SCREEN, id);
+                byte[] blob = packScreen(updated);
+                String backupKey = keyToBackupKey(key);
+                data.writeEntityHeader(backupKey, blob.length);
+                data.writeEntityData(blob, blob.length);
+                out.rows++;
+                out.bytes += blob.length;
+                Log.v(TAG, "saving screen " + backupKey + ": " + id + "/" + blob.length);
+                if(DEBUG) Log.d(TAG, "wrote " +
+                        Base64.encodeToString(blob, 0, blob.length, Base64.NO_WRAP));
+                // remember that is was a new column, so we don't delete it.
+                savedIds.add(backupKey);
+            }
+        } finally {
+            updated.close();
+        }
+        if (DEBUG) Log.d(TAG, "screen savedIds.size()=" + savedIds.size());
+
+        // build the current ID set
+        String idOrder = BaseColumns._ID;
+        Cursor idCursor = cr.query(WorkspaceScreens.CONTENT_URI, ID_ONLY_PROJECTION,
+                null, null, idOrder);
+        idCursor.moveToPosition(-1);
+        Set<String> currentIds = new HashSet<String>(idCursor.getCount());
+        try {
+            while(idCursor.moveToNext()) {
+                Key key = getKey(Key.SCREEN, idCursor.getLong(ID_INDEX));
+                currentIds.add(keyToBackupKey(key));
+                // save the IDs for next time
+                keys.add(key);
+            }
+        } finally {
+            idCursor.close();
+        }
+        if (DEBUG) Log.d(TAG, "screen currentIds.size()=" + currentIds.size());
+
+        // these IDs must have been deleted
+        savedIds.removeAll(currentIds);
+        for(String deleted: savedIds) {
+            Log.v(TAG, "dropping screen " + deleted);
+            data.writeEntityHeader(deleted, -1);
+            out.rows++;
+        }
+    }
+
+    /**
+     * Read a screen from the stream.
+     *
+     * <P>Keys arrive in any order, so children of this screen may already exist.
+     *
+     * @param key identifier for the row
+     * @param buffer the serialized proto from the stream, may be larger than dataSize
+     * @param dataSize the size of the proto from the stream
+     * @param keys keys to mark as clean in the notes for next backup
+     */
+    private void restoreScreen(Key key, byte[] buffer, int dataSize, ArrayList<Key> keys) {
+        Log.v(TAG, "unpacking screen " + key.id);
+        if (DEBUG) Log.d(TAG, "read (" + buffer.length + "): " +
+                Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP));
+        try {
+            Screen screen = unpackScreen(buffer, 0, dataSize);
+            if (DEBUG) Log.d(TAG, "unpacked " + screen.rank);
+        } catch (InvalidProtocolBufferNanoException e) {
+            Log.w(TAG, "failed to decode proto", e);
+        }
+    }
+
+    /** create a new key object.
+     *
+     * <P> Keys contain their own checksum instead of using
+     * the heavy-weight CheckedMessage wrapper.
+     */
+    private Key getKey(int type, long id) {
+        Key key = new Key();
+        key.type = type;
+        key.id = id;
+        key.checksum = checkKey(key);
+        return key;
+    }
+
+    /** keys need to be strings, serialize and encode. */
+    private String keyToBackupKey(Key key) {
+        return Base64.encodeToString(Key.toByteArray(key), Base64.NO_WRAP | Base64.NO_PADDING);
+    }
+
+    /** keys need to be strings, decode and parse. */
+    private Key backupKeyToKey(String backupKey) throws KeyParsingException {
+        try {
+            Key key = Key.parseFrom(Base64.decode(backupKey, Base64.DEFAULT));
+            if (key.checksum != checkKey(key)) {
+                key = null;
+                throw new KeyParsingException("invalid key read from stream" + backupKey);
+            }
+            return key;
+        } catch (InvalidProtocolBufferNanoException e) {
+            throw new KeyParsingException(e);
+        } catch (IllegalArgumentException e) {
+            throw new KeyParsingException(e);
+        }
+    }
+
+    /** Compute the checksum over the important bits of a key. */
+    private long checkKey(Key key) {
+        CRC32 checksum = new CRC32();
+        checksum.update(key.type);
+        checksum.update((int) (key.id & 0xffff));
+        checksum.update((int) ((key.id >> 32) & 0xffff));
+        if (!TextUtils.isEmpty(key.name)) {
+            checksum.update(key.name.getBytes());
+        }
+        return checksum.getValue();
+    }
+
+    /** Serialize a Favorite for persistence, including a checksum wrapper. */
+    private byte[] packFavorite(Cursor c) {
+        Favorite favorite = new Favorite();
+        favorite.id = c.getLong(ID_INDEX);
+        favorite.screen = c.getInt(SCREEN_INDEX);
+        favorite.container = c.getInt(CONTAINER_INDEX);
+        favorite.cellX = c.getInt(CELLX_INDEX);
+        favorite.cellY = c.getInt(CELLY_INDEX);
+        favorite.spanX = c.getInt(SPANX_INDEX);
+        favorite.spanY = c.getInt(SPANY_INDEX);
+        favorite.iconType = c.getInt(ICON_TYPE_INDEX);
+        if (favorite.iconType == Favorites.ICON_TYPE_RESOURCE) {
+            String iconPackage = c.getString(ICON_PACKAGE_INDEX);
+            if (!TextUtils.isEmpty(iconPackage)) {
+                favorite.iconPackage = iconPackage;
+            }
+            String iconResource = c.getString(ICON_RESOURCE_INDEX);
+            if (!TextUtils.isEmpty(iconResource)) {
+                favorite.iconResource = iconResource;
+            }
+        }
+        if (favorite.iconType == Favorites.ICON_TYPE_BITMAP) {
+            byte[] blob = c.getBlob(ICON_INDEX);
+            if (blob != null && blob.length > 0) {
+                favorite.icon = blob;
+            }
+        }
+        String title = c.getString(TITLE_INDEX);
+        if (!TextUtils.isEmpty(title)) {
+            favorite.title = title;
+        }
+        String intent = c.getString(INTENT_INDEX);
+        if (!TextUtils.isEmpty(intent)) {
+            favorite.intent = intent;
+        }
+        favorite.itemType = c.getInt(ITEM_TYPE_INDEX);
+        if (favorite.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
+            favorite.appWidgetId = c.getInt(APPWIDGET_ID_INDEX);
+            String appWidgetProvider = c.getString(APPWIDGET_PROVIDER_INDEX);
+            if (!TextUtils.isEmpty(appWidgetProvider)) {
+                favorite.appWidgetProvider = appWidgetProvider;
+            }
+        }
+
+        return writeCheckedBytes(favorite);
+    }
+
+    /** Deserialize a Favorite from persistence, after verifying checksum wrapper. */
+    private Favorite unpackFavorite(byte[] buffer, int offset, int dataSize)
+            throws InvalidProtocolBufferNanoException {
+        Favorite favorite = new Favorite();
+        MessageNano.mergeFrom(favorite, readCheckedBytes(buffer, offset, dataSize));
+        return favorite;
+    }
+
+    /** Serialize a Screen for persistence, including a checksum wrapper. */
+    private byte[] packScreen(Cursor c) {
+        Screen screen = new Screen();
+        screen.id = c.getLong(ID_INDEX);
+        screen.rank = c.getInt(SCREEN_RANK_INDEX);
+
+        return writeCheckedBytes(screen);
+    }
+
+    /** Deserialize a Screen from persistence, after verifying checksum wrapper. */
+    private Screen unpackScreen(byte[] buffer, int offset, int dataSize)
+            throws InvalidProtocolBufferNanoException {
+        Screen screen = new Screen();
+        MessageNano.mergeFrom(screen, readCheckedBytes(buffer, offset, dataSize));
+        return screen;
+    }
+
+    /**
+     * Read the old journal from the input file.
+     *
+     * In the event of any error, just pretend we didn't have a journal,
+     * in that case, do a full backup.
+     *
+     * @param oldState the read-0only file descriptor pointing to the old journal
+     * @return a Journal protocol bugffer
+     */
+    private Journal readJournal(ParcelFileDescriptor oldState) {
+        int fileSize = (int) oldState.getStatSize();
+        int remaining = fileSize;
+        byte[] buffer = null;
+        Journal journal = new Journal();
+        if (remaining < MAX_JOURNAL_SIZE) {
+            FileInputStream inStream = new FileInputStream(oldState.getFileDescriptor());
+            int offset = 0;
+
+            buffer = new byte[remaining];
+            while (remaining > 0) {
+                int bytesRead = 0;
+                try {
+                    bytesRead = inStream.read(buffer, offset, remaining);
+                } catch (IOException e) {
+                    Log.w(TAG, "failed to read the journal", e);
+                    buffer = null;
+                    remaining = 0;
+                }
+                if (bytesRead > 0) {
+                    remaining -= bytesRead;
+                } else {
+                    // act like there is not journal
+                    Log.w(TAG, "failed to read the journal");
+                    buffer = null;
+                    remaining = 0;
+                }
+            }
+
+            if (buffer != null) {
+                try {
+                    MessageNano.mergeFrom(journal, readCheckedBytes(buffer, 0, fileSize));
+                } catch (InvalidProtocolBufferNanoException e) {
+                    Log.d(TAG, "failed to read the journal", e);
+                    journal.clear();
+                }
+            }
+
+            try {
+                inStream.close();
+            } catch (IOException e) {
+                Log.d(TAG, "failed to close the journal", e);
+            }
+        }
+        return journal;
+    }
+
+    /**
+     * Write the new journal to the output file.
+     *
+     * In the event of any error, just pretend we didn't have a journal,
+     * in that case, do a full backup.
+
+     * @param newState the write-only file descriptor pointing to the new journal
+     * @param journal a Journal protocol buffer
+     */
+    private void writeJournal(ParcelFileDescriptor newState, Journal journal) {
+        FileOutputStream outStream = null;
+        try {
+            outStream = new FileOutputStream(newState.getFileDescriptor());
+            outStream.write(writeCheckedBytes(journal));
+            outStream.close();
+        } catch (IOException e) {
+            Log.d(TAG, "failed to write backup journal", e);
+        }
+    }
+
+    /** Wrap a proto in a CheckedMessage and compute the checksum. */
+    private byte[] writeCheckedBytes(MessageNano proto) {
+        CheckedMessage wrapper = new CheckedMessage();
+        wrapper.payload = MessageNano.toByteArray(proto);
+        CRC32 checksum = new CRC32();
+        checksum.update(wrapper.payload);
+        wrapper.checksum = checksum.getValue();
+        return MessageNano.toByteArray(wrapper);
+    }
+
+    /** Unwrap a proto message from a CheckedMessage, verifying the checksum. */
+    private byte[] readCheckedBytes(byte[] buffer, int offset, int dataSize)
+            throws InvalidProtocolBufferNanoException {
+        CheckedMessage wrapper = new CheckedMessage();
+        MessageNano.mergeFrom(wrapper, buffer, offset, dataSize);
+        CRC32 checksum = new CRC32();
+        checksum.update(wrapper.payload);
+        if (wrapper.checksum != checksum.getValue()) {
+            throw new InvalidProtocolBufferNanoException("checksum does not match");
+        }
+        return wrapper.payload;
+    }
+
+    private class KeyParsingException extends Throwable {
+        private KeyParsingException(Throwable cause) {
+            super(cause);
+        }
+
+        public KeyParsingException(String reason) {
+            super(reason);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index bc0d1bc..1d264aa 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -58,6 +58,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Maintains in-memory state of the Launcher. It is expected that there should be only one
@@ -169,6 +170,7 @@
                         boolean matchPackageNamesOnly);
         public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
         public void bindSearchablesChanged();
+        public boolean isAllAppsButtonRank(int rank);
         public void onPageBoundSynchronously(int page);
         public void dumpLogsToLocalData();
     }
@@ -1513,7 +1515,8 @@
         }
 
         // check & update map of what's occupied; used to discard overlapping/invalid items
-        private boolean checkItemPlacement(HashMap<Long, ItemInfo[][]> occupied, ItemInfo item) {
+        private boolean checkItemPlacement(HashMap<Long, ItemInfo[][]> occupied, ItemInfo item,
+                                           AtomicBoolean deleteOnItemOverlap) {
             LauncherAppState app = LauncherAppState.getInstance();
             DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
             int countX = (int) grid.numColumns;
@@ -1521,6 +1524,13 @@
 
             long containerIndex = item.screenId;
             if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+                // Return early if we detect that an item is under the hotseat button
+                if (mCallbacks == null ||
+                        mCallbacks.get().isAllAppsButtonRank((int) item.screenId)) {
+                    deleteOnItemOverlap.set(true);
+                    return false;
+                }
+
                 if (occupied.containsKey(LauncherSettings.Favorites.CONTAINER_HOTSEAT)) {
                     if (occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT)
                             [(int) item.screenId][0] != null) {
@@ -1658,6 +1668,7 @@
                     Intent intent;
 
                     while (!mStopped && c.moveToNext()) {
+                        AtomicBoolean deleteOnItemOverlap = new AtomicBoolean(false);
                         try {
                             int itemType = c.getInt(itemTypeIndex);
 
@@ -1672,9 +1683,8 @@
                                     if (cn != null && !isValidPackageComponent(manager, cn)) {
                                         if (!mAppsCanBeOnRemoveableStorage) {
                                             // Log the invalid package, and remove it from the db
-                                            Uri uri = LauncherSettings.Favorites.getContentUri(id,
-                                                    false);
-                                            contentResolver.delete(uri, null, null);
+                                            Launcher.addDumpLog(TAG, "Invalid package removed: " + cn, true);
+                                            itemsToRemove.add(id);
                                         } else {
                                             // If apps can be on external storage, then we just
                                             // leave them for the user to remove (maybe add
@@ -1728,7 +1738,11 @@
                                         }
                                     }
                                     // check & update map of what's occupied
-                                    if (!checkItemPlacement(occupied, info)) {
+                                    deleteOnItemOverlap.set(false);
+                                    if (!checkItemPlacement(occupied, info, deleteOnItemOverlap)) {
+                                        if (deleteOnItemOverlap.get()) {
+                                            itemsToRemove.add(id);
+                                        }
                                         break;
                                     }
 
@@ -1776,7 +1790,12 @@
                                     }
                                 }
                                 // check & update map of what's occupied
-                                if (!checkItemPlacement(occupied, folderInfo)) {
+                                deleteOnItemOverlap.set(false);
+                                if (!checkItemPlacement(occupied, folderInfo,
+                                        deleteOnItemOverlap)) {
+                                    if (deleteOnItemOverlap.get()) {
+                                        itemsToRemove.add(id);
+                                    }
                                     break;
                                 }
 
@@ -1838,7 +1857,12 @@
                                         }
                                     }
                                     // check & update map of what's occupied
-                                    if (!checkItemPlacement(occupied, appWidgetInfo)) {
+                                    deleteOnItemOverlap.set(false);
+                                    if (!checkItemPlacement(occupied, appWidgetInfo,
+                                            deleteOnItemOverlap)) {
+                                        if (deleteOnItemOverlap.get()) {
+                                            itemsToRemove.add(id);
+                                        }
                                         break;
                                     }
                                     String providerName = provider.provider.flattenToString();
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index fa61b11..471d2d2 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -49,6 +49,7 @@
 import android.util.Log;
 import android.util.Xml;
 
+import com.android.launcher3.LauncherSettings.BaseLauncherColumns;
 import com.android.launcher3.LauncherSettings.Favorites;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -65,7 +66,7 @@
 
     private static final String DATABASE_NAME = "launcher.db";
 
-    private static final int DATABASE_VERSION = 14;
+    private static final int DATABASE_VERSION = 15;
 
     static final String OLD_AUTHORITY = "com.android.launcher2.settings";
     static final String AUTHORITY = "com.android.launcher3.settings";
@@ -146,6 +147,7 @@
         SqlArguments args = new SqlArguments(uri);
 
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        addModifiedTime(initialValues);
         final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
         if (rowId <= 0) return null;
 
@@ -164,6 +166,7 @@
         try {
             int numValues = values.length;
             for (int i = 0; i < numValues; i++) {
+                addModifiedTime(values[i]);
                 if (dbInsertAndCheck(mOpenHelper, db, args.table, null, values[i]) < 0) {
                     return 0;
                 }
@@ -192,6 +195,7 @@
     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
         SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
 
+        addModifiedTime(values);
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         int count = db.update(args.table, values, args.where, args.args);
         if (count > 0) sendNotify(uri);
@@ -204,6 +208,13 @@
         if (notify == null || "true".equals(notify)) {
             getContext().getContentResolver().notifyChange(uri, null);
         }
+
+        // always notify the backup agent
+        LauncherBackupAgent.dataChanged(getContext());
+    }
+
+    private void addModifiedTime(ContentValues values) {
+        values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis());
     }
 
     public long generateNewItemId() {
@@ -343,7 +354,8 @@
                     "icon BLOB," +
                     "uri TEXT," +
                     "displayMode INTEGER," +
-                    "appWidgetProvider TEXT" +
+                    "appWidgetProvider TEXT," +
+                    "modified INTEGER NOT NULL DEFAULT 0" +
                     ");");
             addWorkspacesTable(db);
 
@@ -384,7 +396,8 @@
         private void addWorkspacesTable(SQLiteDatabase db) {
             db.execSQL("CREATE TABLE " + TABLE_WORKSPACE_SCREENS + " (" +
                     LauncherSettings.WorkspaceScreens._ID + " INTEGER," +
-                    LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER" +
+                    LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," +
+                    LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" +
                     ");");
         }
 
@@ -643,6 +656,25 @@
                 }
             }
 
+
+            if (version < 15) {
+                db.beginTransaction();
+                try {
+                    // Insert new column for holding update timestamp
+                    db.execSQL("ALTER TABLE favorites " +
+                            "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
+                    db.execSQL("ALTER TABLE workspaceScreens " +
+                            "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
+                    db.setTransactionSuccessful();
+                    version = 15;
+                } catch (SQLException ex) {
+                    // Old version remains, which means we wipe old data
+                    Log.e(TAG, ex.getMessage(), ex);
+                } finally {
+                    db.endTransaction();
+                }
+            }
+
             if (version != DATABASE_VERSION) {
                 Log.w(TAG, "Destroying all old data.");
                 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index ada09e9..988e5ef 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -23,7 +23,16 @@
  * Settings related utilities.
  */
 class LauncherSettings {
-    static interface BaseLauncherColumns extends BaseColumns {
+    /** Columns required on table staht will be subject to backup and restore. */
+    static interface ChangeLogColumns extends BaseColumns {
+        /**
+         * The time of the last update to this row.
+         * <P>Type: INTEGER</P>
+         */
+        static final String MODIFIED = "modified";
+    }
+
+    static interface BaseLauncherColumns extends ChangeLogColumns {
         /**
          * Descriptive name of the gesture that can be displayed to the user.
          * <P>Type: TEXT</P>
@@ -95,7 +104,7 @@
      *
      * Tracks the order of workspace screens.
      */
-    static final class WorkspaceScreens implements BaseColumns {
+    static final class WorkspaceScreens implements ChangeLogColumns {
         /**
          * The content:// style URL for this table
          */
diff --git a/src/com/android/launcher3/LiveWallpaperListAdapter.java b/src/com/android/launcher3/LiveWallpaperListAdapter.java
index 9d0f48b..e9e5e79 100644
--- a/src/com/android/launcher3/LiveWallpaperListAdapter.java
+++ b/src/com/android/launcher3/LiveWallpaperListAdapter.java
@@ -116,6 +116,7 @@
             mThumbnail = thumbnail;
             mInfo = info;
         }
+        @Override
         public void onClick(WallpaperPickerActivity a) {
             Intent preview = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
             preview.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT,
diff --git a/src/com/android/launcher3/MarketUpdateReceiver.java b/src/com/android/launcher3/MarketUpdateReceiver.java
new file mode 100644
index 0000000..c41bf3e
--- /dev/null
+++ b/src/com/android/launcher3/MarketUpdateReceiver.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.util.Log;
+
+public class MarketUpdateReceiver extends BroadcastReceiver {
+    private static final String TAG = "MarketUpdateReceiver";
+
+    private static final String ACTION_PACKAGE_ENQUEUED =
+            "com.android.launcher.action.ACTION_PACKAGE_ENQUEUED";
+    private static final String ACTION_PACKAGE_DOWNLOADING =
+            "com.android.launcher.action.ACTION_PACKAGE_DOWNLOADING";
+    private static final String ACTION_PACKAGE_INSTALLING =
+            "com.android.launcher.action.ACTION_PACKAGE_INSTALLING";
+    private static final String ACTION_PACKAGE_DEQUEUED =
+            "com.android.launcher.action.ACTION_PACKAGE_DEQUEUED";
+
+    /** extra for {@link #ACTION_PACKAGE_ENQUEUED}, send on of the following values **/
+    private static final String EXTRA_KEY_REASON = "reason";
+    private static final String EXTRA_VALUE_REASON_INSTALL = "install";
+    private static final String EXTRA_VALUE_REASON_UPDATE = "update";
+    private static final String EXTRA_VALUE_REASON_RESTORE = "restore";
+
+    /** extra for {@link #ACTION_PACKAGE_DOWNLOADING}, send an int in the range [0-100]. **/
+    private static final String EXTRA_KEY_PROGRESS = "progress";
+
+    /**
+     * extra for {@link #ACTION_PACKAGE_DEQUEUED}
+     * send {@link android.app.Activity#RESULT_OK} on success.
+     * or {@link android.app.Activity#RESULT_CANCELED} if the package was abandoned.
+     * **/
+    private static final String EXTRA_KEY_STATUS =
+            "com.android.launcher.action.EXTRA_STATUS";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        final String action = intent.getAction();
+        String pkgName = "none";
+        Uri uri = intent.getData();
+        if (uri != null) {
+            pkgName = uri.getSchemeSpecificPart();;
+        }
+        if (ACTION_PACKAGE_ENQUEUED.equals(action)) {
+            String reason = "unknown";
+            if (intent.hasExtra(EXTRA_KEY_REASON)) {
+                reason = intent.getStringExtra(EXTRA_KEY_REASON);
+            }
+            Log.d(TAG, "market has promised to " + reason + ": " + pkgName);
+        } else if (ACTION_PACKAGE_DOWNLOADING.equals(action)) {
+            int progress = intent.getIntExtra(EXTRA_KEY_PROGRESS, 0);
+            Log.d(TAG, "market is downloading (" + progress + "%): " + pkgName);
+        } else if (ACTION_PACKAGE_INSTALLING.equals(action)) {
+            Log.d(TAG, "market is installing: " + pkgName);
+        } else if ( ACTION_PACKAGE_DEQUEUED.equals(action)) {
+            boolean success = Activity.RESULT_OK == intent.getIntExtra(EXTRA_KEY_STATUS,
+                    Activity.RESULT_CANCELED);
+            if (success) {
+                Log.d(TAG, "market has installed: " + pkgName);
+            } else {
+                Log.d(TAG, "market has decided not to install: " + pkgName);
+            }
+        } else {
+            Log.d(TAG, "unknown message " + action);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 31a9797..1ae2943 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -911,8 +911,9 @@
                 mPageScrolls[i] = childLeft - scrollOffset - offsetX;
 
                 if (i != endIndex - delta) {
+                    childLeft += childWidth + scrollOffset;
                     int nextScrollOffset = (getViewportWidth() - getChildWidth(i + delta)) / 2;
-                    childLeft += childWidth + nextScrollOffset;
+                    childLeft += nextScrollOffset;
                 }
             }
         }
@@ -1039,9 +1040,6 @@
     protected int getChildOffset(int index) {
         if (index < 0 || index > getChildCount() - 1) return 0;
 
-        final boolean isRtl = isLayoutRtl();
-
-        if (isRtl) index = getChildCount() - index - 1;
         int offset = getPageAt(index).getLeft() - getViewportOffsetX();
 
         return offset;
@@ -1056,33 +1054,43 @@
         final int pageCount = getChildCount();
         mTmpIntPoint[0] = mTmpIntPoint[1] = 0;
 
+        range[0] = -1;
+        range[1] = -1;
+
         if (pageCount > 0) {
             int viewportWidth = getViewportWidth();
-            int leftScreen = 0;
-            int rightScreen = 0;
+            int curScreen = 0;
 
-            for (leftScreen = getNextPage(); leftScreen >= 0; --leftScreen) {
-                View currPage = getPageAt(leftScreen);
+            int count = getChildCount();
+            for (int i = 0; i < count; i++) {
+                View currPage = getPageAt(i);
 
-                // Check if the right edge of the page is in the viewport
+                mTmpIntPoint[0] = 0;
+                Utilities.getDescendantCoordRelativeToParent(currPage, this, mTmpIntPoint, false);
+                if (mTmpIntPoint[0] > viewportWidth) {
+                    if (range[0] == -1) {
+                        continue;
+                    } else {
+                        break;
+                    }
+                }
+
                 mTmpIntPoint[0] = currPage.getMeasuredWidth();
                 Utilities.getDescendantCoordRelativeToParent(currPage, this, mTmpIntPoint, false);
                 if (mTmpIntPoint[0] < 0) {
-                    break;
+                    if (range[0] == -1) {
+                        continue;
+                    } else {
+                        break;
+                    }
+                }
+                curScreen = i;
+                if (range[0] < 0) {
+                    range[0] = curScreen;
                 }
             }
-            for (rightScreen = getNextPage(); rightScreen < getChildCount(); ++rightScreen) {
-                View currPage = getPageAt(rightScreen);
 
-                // Check if the left edge of the page is in the viewport
-                mTmpIntPoint[0] = 0;
-                Utilities.getDescendantCoordRelativeToParent(currPage, this, mTmpIntPoint, false);
-                if (mTmpIntPoint[0] >= viewportWidth) {
-                    break;
-                }
-            }
-            range[0] = Math.max(0, leftScreen);
-            range[1] = Math.min(rightScreen, getChildCount() - 1);
+            range[1] = curScreen;
         } else {
             range[0] = -1;
             range[1] = -1;
@@ -1558,8 +1566,13 @@
         if (!mFreeScroll) {
             snapToPage(snapPage);
         } else {
-            mFreeScrollMinScrollX = getScrollForPage(mTempVisiblePagesRange[0]);
-            mFreeScrollMaxScrollX = getScrollForPage(mTempVisiblePagesRange[1]);
+            if (isLayoutRtl()) {
+                mFreeScrollMinScrollX = getScrollForPage(mTempVisiblePagesRange[1]);
+                mFreeScrollMaxScrollX = getScrollForPage(mTempVisiblePagesRange[0]);
+            } else {
+                mFreeScrollMinScrollX = getScrollForPage(mTempVisiblePagesRange[0]);
+                mFreeScrollMaxScrollX = getScrollForPage(mTempVisiblePagesRange[1]);
+            }
 
             if (getCurrentPage() < mTempVisiblePagesRange[0]) {
                 setCurrentPage(mTempVisiblePagesRange[0]);
diff --git a/src/com/android/launcher3/PagedViewIcon.java b/src/com/android/launcher3/PagedViewIcon.java
index fa9ec5a..c6d5e49 100644
--- a/src/com/android/launcher3/PagedViewIcon.java
+++ b/src/com/android/launcher3/PagedViewIcon.java
@@ -18,6 +18,9 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Region;
+import android.graphics.Region.Op;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.widget.TextView;
@@ -99,4 +102,27 @@
             setAlpha(1f);
         }
     }
+
+    @Override
+    public void draw(Canvas canvas) {
+        // If text is transparent, don't draw any shadow
+        if (getCurrentTextColor() == getResources().getColor(android.R.color.transparent)) {
+            getPaint().clearShadowLayer();
+            super.draw(canvas);
+            return;
+        }
+
+        // We enhance the shadow by drawing the shadow twice
+        getPaint().setShadowLayer(BubbleTextView.SHADOW_LARGE_RADIUS, 0.0f,
+                BubbleTextView.SHADOW_Y_OFFSET, BubbleTextView.SHADOW_LARGE_COLOUR);
+        super.draw(canvas);
+        canvas.save(Canvas.CLIP_SAVE_FLAG);
+        canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
+                getScrollX() + getWidth(),
+                getScrollY() + getHeight(), Region.Op.INTERSECT);
+        getPaint().setShadowLayer(BubbleTextView.SHADOW_SMALL_RADIUS, 0.0f, 0.0f,
+                BubbleTextView.SHADOW_SMALL_COLOUR);
+        super.draw(canvas);
+        canvas.restore();
+    }
 }
diff --git a/src/com/android/launcher3/SavedWallpaperImages.java b/src/com/android/launcher3/SavedWallpaperImages.java
index f00f62f..7cd82f0 100644
--- a/src/com/android/launcher3/SavedWallpaperImages.java
+++ b/src/com/android/launcher3/SavedWallpaperImages.java
@@ -57,6 +57,7 @@
             mDbId = dbId;
             mThumb = thumb;
         }
+        @Override
         public void onClick(WallpaperPickerActivity a) {
             String imageFilename = a.getSavedImages().getImageFilename(mDbId);
             File file = new File(a.getFilesDir(), imageFilename);
@@ -65,14 +66,17 @@
             v.moveToLeft();
             v.setTouchEnabled(false);
         }
+        @Override
         public void onSave(WallpaperPickerActivity a) {
             boolean finishActivityWhenDone = true;
             String imageFilename = a.getSavedImages().getImageFilename(mDbId);
             a.setWallpaper(imageFilename, finishActivityWhenDone);
         }
+        @Override
         public void onDelete(WallpaperPickerActivity a) {
             a.getSavedImages().deleteImage(mDbId);
         }
+        @Override
         public boolean isSelectable() {
             return true;
         }
diff --git a/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java b/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java
index ab2f5d7..7ed1c1b 100644
--- a/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java
+++ b/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java
@@ -46,6 +46,7 @@
         public ThirdPartyWallpaperTile(ResolveInfo resolveInfo) {
             mResolveInfo = resolveInfo;
         }
+        @Override
         public void onClick(WallpaperPickerActivity a) {
             final ComponentName itemComponentName = new ComponentName(
                     mResolveInfo.activityInfo.packageName, mResolveInfo.activityInfo.name);
diff --git a/src/com/android/launcher3/WallpaperPickerActivity.java b/src/com/android/launcher3/WallpaperPickerActivity.java
index bc56b55..7f82a2f 100644
--- a/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -87,11 +87,12 @@
     public static abstract class WallpaperTileInfo {
         public void onClick(WallpaperPickerActivity a) {}
         public void onSave(WallpaperPickerActivity a) {}
-        public void onDelete() {}
+        public void onDelete(WallpaperPickerActivity a) {}
         public boolean isSelectable() { return false; }
     }
 
     public static class PickImageInfo extends WallpaperTileInfo {
+        @Override
         public void onClick(WallpaperPickerActivity a) {
             Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
             intent.setType("image/*");
@@ -104,13 +105,14 @@
         public UriWallpaperInfo(Uri uri) {
             mUri = uri;
         }
+        @Override
         public void onClick(WallpaperPickerActivity a) {
             CropView v = a.getCropView();
             v.setTileSource(new BitmapRegionTileSource(
                     a, mUri, 1024, 0), null);
             v.setTouchEnabled(true);
         }
-
+        @Override
         public void onSave(final WallpaperPickerActivity a) {
             boolean finishActivityWhenDone = true;
             OnBitmapCroppedHandler h = new OnBitmapCroppedHandler() {
@@ -123,6 +125,7 @@
             };
             a.cropImageAndSetWallpaper(mUri, h, finishActivityWhenDone);
         }
+        @Override
         public boolean isSelectable() {
             return true;
         }
@@ -138,6 +141,7 @@
             mResId = resId;
             mThumb = thumb;
         }
+        @Override
         public void onClick(WallpaperPickerActivity a) {
             BitmapRegionTileSource source = new BitmapRegionTileSource(
                     mResources, a, mResId, 1024, 0);
@@ -151,10 +155,12 @@
             v.setScale(wallpaperSize.x / crop.width());
             v.setTouchEnabled(false);
         }
+        @Override
         public void onSave(WallpaperPickerActivity a) {
             boolean finishActivityWhenDone = true;
             a.cropImageAndSetWallpaper(mResources, mResId, finishActivityWhenDone);
         }
+        @Override
         public boolean isSelectable() {
             return true;
         }
@@ -363,7 +369,8 @@
                         CheckableFrameLayout c =
                                 (CheckableFrameLayout) mWallpapersView.getChildAt(i);
                         if (c.isChecked()) {
-                            ((WallpaperTileInfo) c.getTag()).onDelete();
+                            WallpaperTileInfo info = (WallpaperTileInfo) c.getTag();
+                            info.onDelete(WallpaperPickerActivity.this);
                             viewsToRemove.add(c);
                         }
                     }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index aab0a63..ada41a0 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -933,8 +933,9 @@
         boolean passRightSwipesToCustomContent =
                 (mTouchDownTime - mCustomContentShowTime) > CUSTOM_CONTENT_GESTURE_DELAY;
 
-        if (deltaX > 0 && getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID
-                && passRightSwipesToCustomContent) {
+        boolean swipeInIgnoreDirection = isLayoutRtl() ? deltaX < 0 : deltaX > 0;
+        if (swipeInIgnoreDirection && getScreenIdForPageIndex(getCurrentPage()) ==
+                CUSTOM_CONTENT_SCREEN_ID && passRightSwipesToCustomContent) {
             // Pass swipes to the right to the custom content page.
             return;
         }
@@ -1363,9 +1364,15 @@
         if (hasCustomContent()) {
             int index = mScreenOrder.indexOf(CUSTOM_CONTENT_SCREEN_ID);
             int scrollDelta = getScrollForPage(index + 1) - getScrollX();
-            translationX = Math.max(scrollDelta, 0);
+            translationX = scrollDelta;
             progress = (1.0f * scrollDelta) /
                     (getScrollForPage(index + 1) - getScrollForPage(index));
+
+            if (isLayoutRtl()) {
+                translationX = Math.min(0, translationX);
+            } else {
+                translationX = Math.max(0, translationX);
+            }
             progress = Math.max(0, progress);
         }
 
@@ -1396,7 +1403,10 @@
         updateStateForCustomContent(screenCenter);
         enableHwLayersOnVisiblePages();
 
-        if ((mOverScrollX < 0 && !hasCustomContent()) || mOverScrollX > mMaxScrollX) {
+        boolean shouldOverScroll = (mOverScrollX < 0 && (!hasCustomContent() || isLayoutRtl())) ||
+                (mOverScrollX > mMaxScrollX && (!hasCustomContent() || !isLayoutRtl()));
+
+        if (shouldOverScroll) {
             int index = 0;
             float pivotX = 0f;
             final float leftBiasedPivot = 0.25f;
@@ -1836,10 +1846,10 @@
         final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED);
         final boolean stateIsSmall = (state == State.SMALL);
         final boolean stateIsOverview = (state == State.OVERVIEW);
-        float finalBackgroundAlpha = stateIsSpringLoaded ? 1.0f : 0f;
+        float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f;
         float finalHotseatAndPageIndicatorAlpha = (stateIsOverview || stateIsSmall) ? 0f : 1f;
         float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f;
-        float finalSearchBarAlpha = stateIsOverview ? 0f : 1f;
+        float finalSearchBarAlpha = !stateIsNormal ? 0f : 1f;
         float finalWorkspaceTranslationY = stateIsOverview ? getOverviewModeTranslationY() : 0;
 
         boolean zoomIn = true;
@@ -1857,13 +1867,11 @@
             } else if (stateIsOverview) {
                 mNewScale = mOverviewModeShrinkFactor;
             } else if (stateIsSmall){
-                mNewScale = mOverviewModeShrinkFactor - 0.1f;
+                mNewScale = mOverviewModeShrinkFactor - 0.3f;
             }
             if (oldStateIsNormal && stateIsSmall) {
                 zoomIn = false;
                 updateChildrenLayersEnabled(false);
-            } else {
-                finalBackgroundAlpha = 1.0f;
             }
         }
         final int duration = zoomIn ?
@@ -1871,24 +1879,9 @@
                 getResources().getInteger(R.integer.config_appsCustomizeWorkspaceShrinkTime);
         for (int i = 0; i < getChildCount(); i++) {
             final CellLayout cl = (CellLayout) getChildAt(i);
-            float finalAlpha = (!mWorkspaceFadeInAdjacentScreens || stateIsSpringLoaded ||
-                    (i == mCurrentPage)) ? 1f : 0f;
-            float currentAlpha = cl.getShortcutsAndWidgets().getAlpha();
-            float initialAlpha = currentAlpha;
-
-            // Determine the pages alpha during the state transition
-            if ((oldStateIsSmall && stateIsNormal) ||
-                (oldStateIsNormal && stateIsSmall)) {
-                // To/from workspace - only show the current page unless the transition is not
-                //                     animated and the animation end callback below doesn't run;
-                //                     or, if we're in spring-loaded mode
-                if (i == mCurrentPage || !animated || oldStateIsSpringLoaded) {
-                    finalAlpha = 1f;
-                } else {
-                    initialAlpha = 0f;
-                    finalAlpha = 0f;
-                }
-            }
+            float finalAlpha = (!mWorkspaceFadeInAdjacentScreens ||
+                    (i == mCurrentPage)) && !stateIsSmall ? 1f : 0f;
+            float initialAlpha = cl.getShortcutsAndWidgets().getAlpha();
 
             mOldAlphas[i] = initialAlpha;
             mNewAlphas[i] = finalAlpha;
@@ -1901,7 +1894,9 @@
             }
         }
 
-        View searchBar = mLauncher.getQsbBar();
+        final View searchBar = mLauncher.getQsbBar();
+        final View overviewPanel = mLauncher.getOverviewPanel();
+        final View hotseat = mLauncher.getHotseat();
         if (animated) {
             LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(this);
             scale.scaleX(mNewScale)
@@ -1917,7 +1912,6 @@
                     cl.setBackgroundAlpha(mNewBackgroundAlphas[i]);
                     cl.setShortcutAndWidgetAlpha(mNewAlphas[i]);
                 } else {
-
                     if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) {
                         LauncherViewPropertyAnimator alphaAnim =
                             new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets());
@@ -1947,29 +1941,32 @@
                 pageIndicatorAlpha = ObjectAnimator.ofFloat(getPageIndicator(), "alpha",
                         finalHotseatAndPageIndicatorAlpha);
             }
-            ObjectAnimator hotseatAlpha = ObjectAnimator.ofFloat(mLauncher.getHotseat(), "alpha",
+            ObjectAnimator hotseatAlpha = ObjectAnimator.ofFloat(hotseat, "alpha",
                     finalHotseatAndPageIndicatorAlpha);
             ObjectAnimator searchBarAlpha = ObjectAnimator.ofFloat(searchBar,
                     "alpha", finalSearchBarAlpha);
-            ObjectAnimator overviewPanelAlpha = ObjectAnimator.ofFloat(mLauncher.getOverviewPanel(),
+            ObjectAnimator overviewPanelAlpha = ObjectAnimator.ofFloat(overviewPanel,
                     "alpha", finalOverviewPanelAlpha);
-            overviewPanelAlpha.addUpdateListener(new AlphaUpdateListener(
-                    mLauncher.getOverviewPanel()));
-            hotseatAlpha.addUpdateListener(new AlphaUpdateListener(mLauncher.getHotseat()));
+
+            overviewPanelAlpha.addUpdateListener(new AlphaUpdateListener(overviewPanel));
+            hotseatAlpha.addUpdateListener(new AlphaUpdateListener(hotseat));
             searchBarAlpha.addUpdateListener(new AlphaUpdateListener(searchBar));
+
             if (getPageIndicator() != null) {
                 pageIndicatorAlpha.addUpdateListener(new AlphaUpdateListener(getPageIndicator()));
             }
+
+
             anim.play(overviewPanelAlpha);
             anim.play(hotseatAlpha);
             anim.play(searchBarAlpha);
             anim.play(pageIndicatorAlpha);
             anim.setStartDelay(delay);
         } else {
-            mLauncher.getOverviewPanel().setAlpha(finalOverviewPanelAlpha);
-            AlphaUpdateListener.updateVisibility(mLauncher.getOverviewPanel());
-            mLauncher.getHotseat().setAlpha(finalHotseatAndPageIndicatorAlpha);
-            AlphaUpdateListener.updateVisibility(mLauncher.getHotseat());
+            overviewPanel.setAlpha(finalOverviewPanelAlpha);
+            AlphaUpdateListener.updateVisibility(overviewPanel);
+            hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha);
+            AlphaUpdateListener.updateVisibility(hotseat);
             if (getPageIndicator() != null) {
                 getPageIndicator().setAlpha(finalHotseatAndPageIndicatorAlpha);
                 AlphaUpdateListener.updateVisibility(getPageIndicator());
@@ -2917,7 +2914,10 @@
        mTempPt[0] = x;
        mTempPt[1] = y;
        mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
-       mLauncher.getHotseat().getHitRect(r);
+
+       LauncherAppState app = LauncherAppState.getInstance();
+       DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+       r = grid.getHotseatRect();
        if (r.contains(mTempPt[0], mTempPt[1])) {
            return true;
        }