am 1419f206: (-s ours) Reconcile with jb-mr1-release - do not merge

* commit '1419f206d29c3357f56edda72354d7d305fee974':
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index adb6ef3..c14b63e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -64,7 +64,7 @@
     <application
         android:name="com.android.launcher2.LauncherApplication"
         android:label="@string/application_name"
-        android:icon="@drawable/ic_launcher_home"
+        android:icon="@mipmap/ic_launcher_home"
         android:hardwareAccelerated="true"
         android:largeHeap="@bool/config_largeHeap"
         android:supportsRtl="true">
@@ -88,7 +88,7 @@
             android:name="com.android.launcher2.WallpaperChooser"
             android:theme="@style/Theme.WallpaperPicker"
             android:label="@string/pick_wallpaper"
-            android:icon="@drawable/ic_launcher_wallpaper"
+            android:icon="@mipmap/ic_launcher_wallpaper"
             android:finishOnCloseSystemDialogs="true"
             android:process=":wallpaper_chooser">
             <intent-filter>
diff --git a/res/drawable-hdpi/ic_launcher_wallpaper.png b/res/drawable-hdpi/ic_launcher_wallpaper.png
deleted file mode 100644
index 5c8ee24..0000000
--- a/res/drawable-hdpi/ic_launcher_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_wallpaper.png b/res/drawable-mdpi/ic_launcher_wallpaper.png
deleted file mode 100644
index d2803b1..0000000
--- a/res/drawable-mdpi/ic_launcher_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_wallpaper.png b/res/drawable-xhdpi/ic_launcher_wallpaper.png
deleted file mode 100644
index 9b0b7b2..0000000
--- a/res/drawable-xhdpi/ic_launcher_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 7f705f5..051d8b2 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -14,94 +14,108 @@
      limitations under the License.
 -->
 
-<com.android.launcher2.DragLayer
+<!-- Full screen view projects under the status bar and contains the background -->
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
 
-    android:id="@+id/drag_layer"
-    android:background="@drawable/workspace_bg"
+    android:id="@+id/launcher"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:background="@drawable/workspace_bg">
 
-    <!-- The workspace contains 5 screens of cells -->
-    <com.android.launcher2.Workspace
-        android:id="@+id/workspace"
+    <com.android.launcher2.DragLayer
+        android:id="@+id/drag_layer"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingLeft="@dimen/workspace_left_padding"
-        android:paddingRight="@dimen/workspace_right_padding"
-        android:paddingTop="@dimen/workspace_top_padding"
-        android:paddingBottom="@dimen/workspace_bottom_padding"
-        launcher:defaultScreen="2"
-        launcher:cellCountX="@integer/cell_count_x"
-        launcher:cellCountY="@integer/cell_count_y"
-        launcher:pageSpacing="@dimen/workspace_page_spacing"
-        launcher:scrollIndicatorPaddingLeft="@dimen/qsb_bar_height"
-        launcher:scrollIndicatorPaddingRight="@dimen/button_bar_height">
+        android:fitsSystemWindows="true">
 
-        <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
-        <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
-        <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
-        <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
-        <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
-    </com.android.launcher2.Workspace>
+        <!-- The workspace contains 5 screens of cells -->
+        <com.android.launcher2.Workspace
+            android:id="@+id/workspace"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:paddingLeft="@dimen/workspace_left_padding"
+            android:paddingRight="@dimen/workspace_right_padding"
+            android:paddingTop="@dimen/workspace_top_padding"
+            android:paddingBottom="@dimen/workspace_bottom_padding"
+            launcher:defaultScreen="2"
+            launcher:cellCountX="@integer/cell_count_x"
+            launcher:cellCountY="@integer/cell_count_y"
+            launcher:pageSpacing="@dimen/workspace_page_spacing"
+            launcher:scrollIndicatorPaddingLeft="@dimen/qsb_bar_height"
+            launcher:scrollIndicatorPaddingRight="@dimen/button_bar_height">
 
-    <include
-        android:id="@+id/qsb_divider"
-        layout="@layout/workspace_divider"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_marginLeft="@dimen/qsb_bar_height"
-        android:layout_gravity="left" />
-    <include
-        android:id="@+id/dock_divider"
-        layout="@layout/workspace_divider"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_marginRight="@dimen/button_bar_height"
-        android:layout_gravity="right" />
-    <include
-        android:id="@+id/paged_view_indicator"
-        layout="@layout/scroll_indicator"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="bottom" />
+            <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
+            <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
+            <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
+            <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
+            <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
+        </com.android.launcher2.Workspace>
 
-    <include layout="@layout/hotseat"
-        android:id="@+id/hotseat"
-        android:layout_width="@dimen/button_bar_height_plus_padding"
-        android:layout_height="match_parent"
-        android:layout_gravity="right" />
-    <include
-        android:id="@+id/qsb_bar"
-        layout="@layout/qsb_bar" />
+        <include
+            android:id="@+id/qsb_divider"
+            layout="@layout/workspace_divider"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_marginLeft="@dimen/qsb_bar_height"
+            android:layout_gravity="left" />
 
-    <com.android.launcher2.DrawableStateProxyView
-        android:id="@+id/voice_button_proxy"
-        android:layout_width="@dimen/qsb_bar_height"
-        android:layout_height="@dimen/app_icon_size"
-        android:layout_gravity="top|left"
-        android:layout_marginTop="64dp"
-        android:clickable="true"
-        android:onClick="onClickVoiceButton"
-        android:importantForAccessibility="no"
-        launcher:sourceViewId="@+id/voice_button" />
+        <include
+            android:id="@+id/dock_divider"
+            layout="@layout/workspace_divider"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_marginRight="@dimen/button_bar_height"
+            android:layout_gravity="right" />
 
-    <include layout="@layout/apps_customize_pane"
-        android:id="@+id/apps_customize_pane"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="invisible" />
+        <include
+            android:id="@+id/paged_view_indicator"
+            layout="@layout/scroll_indicator"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom" />
 
-    <include layout="@layout/workspace_cling"
-        android:id="@+id/workspace_cling"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="gone" />
+        <include layout="@layout/hotseat"
+            android:id="@+id/hotseat"
+            android:layout_width="@dimen/button_bar_height_plus_padding"
+            android:layout_height="match_parent"
+            android:layout_gravity="right" />
 
-    <include layout="@layout/folder_cling"
-        android:id="@+id/folder_cling"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="gone" />
-</com.android.launcher2.DragLayer>
+        <include
+            android:id="@+id/qsb_bar"
+            layout="@layout/qsb_bar" />
+
+        <!-- The Workspace cling must appear under the AppsCustomizePagedView below to ensure
+             that it is still visible during the transition to AllApps and doesn't overlay on
+             top of that view. -->
+        <include layout="@layout/workspace_cling"
+            android:id="@+id/workspace_cling"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone" />
+
+        <include layout="@layout/folder_cling"
+            android:id="@+id/folder_cling"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone" />
+
+        <com.android.launcher2.DrawableStateProxyView
+            android:id="@+id/voice_button_proxy"
+            android:layout_width="@dimen/qsb_bar_height"
+            android:layout_height="@dimen/app_icon_size"
+            android:layout_gravity="top|left"
+            android:layout_marginTop="64dp"
+            android:clickable="true"
+            android:onClick="onClickVoiceButton"
+            android:importantForAccessibility="no"
+            launcher:sourceViewId="@+id/voice_button" />
+
+        <include layout="@layout/apps_customize_pane"
+            android:id="@+id/apps_customize_pane"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="invisible" />
+    </com.android.launcher2.DragLayer>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index a427501..445c85e 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -14,90 +14,106 @@
      limitations under the License.
 -->
 
-<com.android.launcher2.DragLayer
+<!-- Full screen view projects under the status bar and contains the background -->
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
 
-    android:id="@+id/drag_layer"
-    android:background="@drawable/workspace_bg"
+    android:id="@+id/launcher"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:background="@drawable/workspace_bg">
 
-    <!-- Keep these behind the workspace so that they are not visible when
-         we go into AllApps -->
-    <include
-        android:id="@+id/dock_divider"
-        layout="@layout/workspace_divider"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/button_bar_height"
-        android:layout_gravity="bottom" />
-    <include
-        android:id="@+id/paged_view_indicator"
-        layout="@layout/scroll_indicator"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="bottom"
-        android:layout_marginBottom="@dimen/button_bar_height" />
+    <com.android.launcher2.DragLayer
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
 
-    <!-- The workspace contains 5 screens of cells -->
-    <com.android.launcher2.Workspace
-        android:id="@+id/workspace"
+        android:id="@+id/drag_layer"
+        android:background="@drawable/workspace_bg"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingLeft="@dimen/workspace_left_padding"
-        android:paddingRight="@dimen/workspace_right_padding"
-        android:paddingTop="@dimen/workspace_top_padding"
-        android:paddingBottom="@dimen/workspace_bottom_padding"
-        launcher:defaultScreen="2"
-        launcher:cellCountX="@integer/cell_count_x"
-        launcher:cellCountY="@integer/cell_count_y"
-        launcher:pageSpacing="@dimen/workspace_page_spacing"
-        launcher:scrollIndicatorPaddingLeft="@dimen/workspace_divider_padding_left"
-        launcher:scrollIndicatorPaddingRight="@dimen/workspace_divider_padding_right">
+        android:fitsSystemWindows="true">
 
-        <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
-        <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
-        <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
-        <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
-        <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
-    </com.android.launcher2.Workspace>
+        <!-- Keep these behind the workspace so that they are not visible when
+             we go into AllApps -->
+        <include
+            android:id="@+id/dock_divider"
+            layout="@layout/workspace_divider"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/button_bar_height"
+            android:layout_gravity="bottom" />
 
-    <include layout="@layout/hotseat"
-        android:id="@+id/hotseat"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/button_bar_height_plus_padding"
-        android:layout_gravity="bottom" />
+        <include
+            android:id="@+id/paged_view_indicator"
+            layout="@layout/scroll_indicator"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:layout_marginBottom="@dimen/button_bar_height" />
 
-    <include
-        android:id="@+id/qsb_bar"
-        layout="@layout/qsb_bar" />
+        <!-- The workspace contains 5 screens of cells -->
+        <com.android.launcher2.Workspace
+            android:id="@+id/workspace"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:paddingLeft="@dimen/workspace_left_padding"
+            android:paddingRight="@dimen/workspace_right_padding"
+            android:paddingTop="@dimen/workspace_top_padding"
+            android:paddingBottom="@dimen/workspace_bottom_padding"
+            launcher:defaultScreen="2"
+            launcher:cellCountX="@integer/cell_count_x"
+            launcher:cellCountY="@integer/cell_count_y"
+            launcher:pageSpacing="@dimen/workspace_page_spacing"
+            launcher:scrollIndicatorPaddingLeft="@dimen/workspace_divider_padding_left"
+            launcher:scrollIndicatorPaddingRight="@dimen/workspace_divider_padding_right">
 
-    <com.android.launcher2.DrawableStateProxyView
-        android:id="@+id/voice_button_proxy"
-        android:layout_width="80dp"
-        android:layout_height="@dimen/qsb_bar_height"
-        android:layout_gravity="top|right"
-        android:clickable="true"
-        android:onClick="onClickVoiceButton"
-        android:importantForAccessibility="no"
-        launcher:sourceViewId="@+id/voice_button" />
+            <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
+            <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
+            <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
+            <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
+            <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
+        </com.android.launcher2.Workspace>
 
-    <include layout="@layout/apps_customize_pane"
-        android:id="@+id/apps_customize_pane"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="invisible" />
+        <include layout="@layout/hotseat"
+            android:id="@+id/hotseat"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/button_bar_height_plus_padding"
+            android:layout_gravity="bottom" />
 
-    <include layout="@layout/workspace_cling"
-        android:id="@+id/workspace_cling"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="gone" />
+        <include
+            android:id="@+id/qsb_bar"
+            layout="@layout/qsb_bar" />
 
-    <include layout="@layout/folder_cling"
-        android:id="@+id/folder_cling"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="gone" />
-</com.android.launcher2.DragLayer>
+        <!-- The Workspace cling must appear under the AppsCustomizePagedView below to ensure
+             that it is still visible during the transition to AllApps and doesn't overlay on
+             top of that view. -->
+        <include layout="@layout/workspace_cling"
+            android:id="@+id/workspace_cling"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone" />
+
+        <include layout="@layout/folder_cling"
+            android:id="@+id/folder_cling"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone" />
+
+        <com.android.launcher2.DrawableStateProxyView
+            android:id="@+id/voice_button_proxy"
+            android:layout_width="80dp"
+            android:layout_height="@dimen/qsb_bar_height"
+            android:layout_gravity="top|right"
+            android:clickable="true"
+            android:onClick="onClickVoiceButton"
+            android:importantForAccessibility="no"
+            launcher:sourceViewId="@+id/voice_button" />
+
+        <include layout="@layout/apps_customize_pane"
+            android:id="@+id/apps_customize_pane"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="invisible" />
+    </com.android.launcher2.DragLayer>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 418469b..81a8f37 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -14,91 +14,107 @@
      limitations under the License.
 -->
 
-<com.android.launcher2.DragLayer
+<!-- Full screen view projects under the status bar and contains the background -->
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
 
-    android:id="@+id/drag_layer"
-    android:background="@drawable/workspace_bg"
+    android:id="@+id/launcher"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:background="@drawable/workspace_bg">
 
-    <!-- Keep these behind the workspace so that they are not visible when
-         we go into AllApps -->
-    <include
-        android:id="@+id/dock_divider"
-        layout="@layout/workspace_divider"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/button_bar_height_plus_padding"
-        android:layout_gravity="bottom|center_horizontal" />
-    <include
-        android:id="@+id/paged_view_indicator"
-        layout="@layout/scroll_indicator"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="bottom"
-        android:layout_marginBottom="@dimen/button_bar_height_plus_padding" />
+    <com.android.launcher2.DragLayer
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
 
-    <!-- The workspace contains 5 screens of cells -->
-    <com.android.launcher2.Workspace
-        android:id="@+id/workspace"
+        android:id="@+id/drag_layer"
+        android:background="@drawable/workspace_bg"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingLeft="@dimen/workspace_left_padding"
-        android:paddingRight="@dimen/workspace_right_padding"
-        android:paddingTop="@dimen/workspace_top_padding"
-        android:paddingBottom="@dimen/workspace_bottom_padding"
-        launcher:defaultScreen="2"
-        launcher:cellCountX="@integer/cell_count_x"
-        launcher:cellCountY="@integer/cell_count_y"
-        launcher:pageSpacing="@dimen/workspace_page_spacing"
-        launcher:scrollIndicatorPaddingLeft="@dimen/workspace_divider_padding_left"
-        launcher:scrollIndicatorPaddingRight="@dimen/workspace_divider_padding_right">
+        android:fitsSystemWindows="true">
 
-        <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
-        <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
-        <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
-        <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
-        <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
-    </com.android.launcher2.Workspace>
+        <!-- Keep these behind the workspace so that they are not visible when
+             we go into AllApps -->
+        <include
+            android:id="@+id/dock_divider"
+            layout="@layout/workspace_divider"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="@dimen/button_bar_height_plus_padding"
+            android:layout_gravity="bottom|center_horizontal" />
 
-    <include layout="@layout/hotseat"
-        android:id="@+id/hotseat"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/button_bar_height_plus_padding"
-        android:layout_gravity="bottom" />
+        <include
+            android:id="@+id/paged_view_indicator"
+            layout="@layout/scroll_indicator"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom"
+            android:layout_marginBottom="@dimen/button_bar_height_plus_padding" />
 
-    <include
-        android:id="@+id/qsb_bar"
-        layout="@layout/qsb_bar" />
+        <!-- The workspace contains 5 screens of cells -->
+        <com.android.launcher2.Workspace
+            android:id="@+id/workspace"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:paddingLeft="@dimen/workspace_left_padding"
+            android:paddingRight="@dimen/workspace_right_padding"
+            android:paddingTop="@dimen/workspace_top_padding"
+            android:paddingBottom="@dimen/workspace_bottom_padding"
+            launcher:defaultScreen="2"
+            launcher:cellCountX="@integer/cell_count_x"
+            launcher:cellCountY="@integer/cell_count_y"
+            launcher:pageSpacing="@dimen/workspace_page_spacing"
+            launcher:scrollIndicatorPaddingLeft="@dimen/workspace_divider_padding_left"
+            launcher:scrollIndicatorPaddingRight="@dimen/workspace_divider_padding_right">
 
-    <com.android.launcher2.DrawableStateProxyView
-        android:id="@+id/voice_button_proxy"
-        android:layout_width="80dp"
-        android:layout_height="@dimen/qsb_bar_height"
-        android:layout_marginRight="@dimen/qsb_voice_proxy_padding_right"
-        android:layout_gravity="top|right"
-        android:clickable="true"
-        android:onClick="onClickVoiceButton"
-        android:importantForAccessibility="no"
-        launcher:sourceViewId="@+id/voice_button" />
+            <include android:id="@+id/cell1" layout="@layout/workspace_screen" />
+            <include android:id="@+id/cell2" layout="@layout/workspace_screen" />
+            <include android:id="@+id/cell3" layout="@layout/workspace_screen" />
+            <include android:id="@+id/cell4" layout="@layout/workspace_screen" />
+            <include android:id="@+id/cell5" layout="@layout/workspace_screen" />
+        </com.android.launcher2.Workspace>
 
-    <include layout="@layout/apps_customize_pane"
-        android:id="@+id/apps_customize_pane"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="invisible" />
+        <include layout="@layout/hotseat"
+            android:id="@+id/hotseat"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/button_bar_height_plus_padding"
+            android:layout_gravity="bottom" />
 
-    <include layout="@layout/workspace_cling"
-        android:id="@+id/workspace_cling"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="gone" />
+        <include
+            android:id="@+id/qsb_bar"
+            layout="@layout/qsb_bar" />
 
-    <include layout="@layout/folder_cling"
-        android:id="@+id/folder_cling"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="gone" />
-</com.android.launcher2.DragLayer>
+        <!-- The Workspace cling must appear under the AppsCustomizePagedView below to ensure
+             that it is still visible during the transition to AllApps and doesn't overlay on
+             top of that view. -->
+        <include layout="@layout/workspace_cling"
+            android:id="@+id/workspace_cling"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone" />
+
+        <include layout="@layout/folder_cling"
+            android:id="@+id/folder_cling"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="gone" />
+
+        <com.android.launcher2.DrawableStateProxyView
+            android:id="@+id/voice_button_proxy"
+            android:layout_width="80dp"
+            android:layout_height="@dimen/qsb_bar_height"
+            android:layout_marginRight="@dimen/qsb_voice_proxy_padding_right"
+            android:layout_gravity="top|right"
+            android:clickable="true"
+            android:onClick="onClickVoiceButton"
+            android:importantForAccessibility="no"
+            launcher:sourceViewId="@+id/voice_button" />
+
+        <include layout="@layout/apps_customize_pane"
+            android:id="@+id/apps_customize_pane"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:visibility="invisible" />
+    </com.android.launcher2.DragLayer>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/drawable-hdpi/ic_launcher_home.png b/res/mipmap-hdpi/ic_launcher_home.png
similarity index 100%
rename from res/drawable-hdpi/ic_launcher_home.png
rename to res/mipmap-hdpi/ic_launcher_home.png
Binary files differ
diff --git a/res/mipmap-hdpi/ic_launcher_wallpaper.png b/res/mipmap-hdpi/ic_launcher_wallpaper.png
new file mode 100644
index 0000000..affee85
--- /dev/null
+++ b/res/mipmap-hdpi/ic_launcher_wallpaper.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_home.png b/res/mipmap-mdpi/ic_launcher_home.png
similarity index 100%
rename from res/drawable-mdpi/ic_launcher_home.png
rename to res/mipmap-mdpi/ic_launcher_home.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_wallpaper.png b/res/mipmap-mdpi/ic_launcher_wallpaper.png
new file mode 100644
index 0000000..cb4443b
--- /dev/null
+++ b/res/mipmap-mdpi/ic_launcher_wallpaper.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_home.png b/res/mipmap-xhdpi/ic_launcher_home.png
similarity index 100%
rename from res/drawable-xhdpi/ic_launcher_home.png
rename to res/mipmap-xhdpi/ic_launcher_home.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_wallpaper.png b/res/mipmap-xhdpi/ic_launcher_wallpaper.png
new file mode 100644
index 0000000..60f8dce
--- /dev/null
+++ b/res/mipmap-xhdpi/ic_launcher_wallpaper.png
Binary files differ
diff --git a/res/mipmap-xxhdpi/ic_launcher_wallpaper.png b/res/mipmap-xxhdpi/ic_launcher_wallpaper.png
new file mode 100644
index 0000000..023fb58
--- /dev/null
+++ b/res/mipmap-xxhdpi/ic_launcher_wallpaper.png
Binary files differ
diff --git a/src/com/android/launcher2/AddAdapter.java b/src/com/android/launcher2/AddAdapter.java
index c2a424b..c0bb17b 100644
--- a/src/com/android/launcher2/AddAdapter.java
+++ b/src/com/android/launcher2/AddAdapter.java
@@ -71,7 +71,7 @@
         Resources res = launcher.getResources();
 
         mItems.add(new ListItem(res, R.string.group_wallpapers,
-                R.drawable.ic_launcher_wallpaper, ITEM_WALLPAPER));
+                R.mipmap.ic_launcher_wallpaper, ITEM_WALLPAPER));
     }
 
     public View getView(int position, View convertView, ViewGroup parent) {
diff --git a/src/com/android/launcher2/AppWidgetResizeFrame.java b/src/com/android/launcher2/AppWidgetResizeFrame.java
index 7762ece..13ee6f9 100644
--- a/src/com/android/launcher2/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher2/AppWidgetResizeFrame.java
@@ -399,8 +399,10 @@
 
     public void snapToWidget(boolean animate) {
         final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
-        int xOffset = mCellLayout.getLeft() + mCellLayout.getPaddingLeft() - mWorkspace.getScrollX();
-        int yOffset = mCellLayout.getTop() + mCellLayout.getPaddingTop() - mWorkspace.getScrollY();
+        int xOffset = mCellLayout.getLeft() + mCellLayout.getPaddingLeft()
+                + mDragLayer.getPaddingLeft() - mWorkspace.getScrollX();
+        int yOffset = mCellLayout.getTop() + mCellLayout.getPaddingTop()
+                + mDragLayer.getPaddingTop() - mWorkspace.getScrollY();
 
         int newWidth = mWidgetView.getWidth() + 2 * mBackgroundPadding - mWidgetPaddingLeft -
                 mWidgetPaddingRight;
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index 5580381..4bfeb60 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -51,7 +51,6 @@
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.AccelerateInterpolator;
@@ -500,9 +499,10 @@
             int[] pos = mWidgetSpacingLayout.estimateCellPosition(mClingFocusedX, mClingFocusedY);
             mLauncher.getDragLayer().getLocationInDragLayer(this, offset);
             // PagedViews are centered horizontally but top aligned
+            // Note we have to shift the items up now that Launcher sits under the status bar
             pos[0] += (getMeasuredWidth() - mWidgetSpacingLayout.getMeasuredWidth()) / 2 +
                     offset[0];
-            pos[1] += offset[1];
+            pos[1] += offset[1] - mLauncher.getDragLayer().getPaddingTop();
             mLauncher.showFirstRunAllAppsCling(pos);
         }
     }
diff --git a/src/com/android/launcher2/AppsCustomizeTabHost.java b/src/com/android/launcher2/AppsCustomizeTabHost.java
index 96ec33e..5eb8483 100644
--- a/src/com/android/launcher2/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher2/AppsCustomizeTabHost.java
@@ -282,10 +282,15 @@
                         reloadCurrentPage();
                     }
                 });
-                AnimatorSet animSet = LauncherAnimUtils.createAnimatorSet();
+
+                final AnimatorSet animSet = LauncherAnimUtils.createAnimatorSet();
                 animSet.playTogether(outAnim, inAnim);
                 animSet.setDuration(duration);
-                animSet.start();
+                post(new Runnable() {
+                    public void run() {
+                        animSet.start();
+                    }
+                });
             }
         });
     }
@@ -413,11 +418,9 @@
         }
 
         if (!toWorkspace) {
-            // Going from Workspace -> All Apps
-            setVisibilityOfSiblingsWithLowerZOrder(INVISIBLE);
-
-            // Dismiss the workspace cling and show the all apps cling (if not already shown)
+            // Dismiss the workspace cling
             l.dismissWorkspaceCling(null);
+            // Show the all apps cling (if not already shown)
             mAppsCustomizePane.showAllAppsCling();
             // Make sure adjacent pages are loaded (we wait until after the transition to
             // prevent slowing down the animation)
@@ -426,6 +429,11 @@
             if (!LauncherApplication.isScreenLarge()) {
                 mAppsCustomizePane.hideScrollingIndicator(false);
             }
+
+            // Going from Workspace -> All Apps
+            // NOTE: We should do this at the end since we check visibility state in some of the
+            // cling initialization/dismiss code above.
+            setVisibilityOfSiblingsWithLowerZOrder(INVISIBLE);
         }
     }
 
diff --git a/src/com/android/launcher2/CellLayout.java b/src/com/android/launcher2/CellLayout.java
index f742255..7818da4 100644
--- a/src/com/android/launcher2/CellLayout.java
+++ b/src/com/android/launcher2/CellLayout.java
@@ -53,6 +53,8 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Stack;
 
@@ -1554,48 +1556,6 @@
         return bestXY;
     }
 
-    private int[] findNearestAreaInDirection(int cellX, int cellY, int spanX, int spanY, 
-            int[] direction,boolean[][] occupied,
-            boolean blockOccupied[][], int[] result) {
-        // Keep track of best-scoring drop area
-        final int[] bestXY = result != null ? result : new int[2];
-        bestXY[0] = -1;
-        bestXY[1] = -1;
-        float bestDistance = Float.MAX_VALUE;
-
-        // We use this to march in a single direction
-        if ((direction[0] != 0 && direction[1] != 0) ||
-                (direction[0] == 0 && direction[1] == 0)) {
-            return bestXY;
-        }
-
-        // This will only incrememnet one of x or y based on the assertion above
-        int x = cellX + direction[0];
-        int y = cellY + direction[1];
-        while (x >= 0 && x + spanX <= mCountX && y >= 0 && y + spanY <= mCountY) {
-            boolean fail = false;
-            for (int i = 0; i < spanX; i++) {
-                for (int j = 0; j < spanY; j++) {
-                    if (occupied[x + i][y + j] && (blockOccupied == null || blockOccupied[i][j])) {
-                        fail = true;                    
-                    }
-                }
-            }
-            if (!fail) {
-                float distance = (float)
-                        Math.sqrt((x - cellX) * (x - cellX) + (y - cellY) * (y - cellY));
-                if (Float.compare(distance,  bestDistance) < 0) {
-                    bestDistance = distance;
-                    bestXY[0] = x;
-                    bestXY[1] = y;
-                }
-            }
-            x += direction[0];
-            y += direction[1];
-        }
-        return bestXY;
-    }
-
     private boolean addViewToTempLocation(View v, Rect rectOccupiedByPotentialDrop,
             int[] direction, ItemConfiguration currentState) {
         CellAndSpan c = currentState.map.get(v);
@@ -1609,118 +1569,343 @@
             c.x = mTempLocation[0];
             c.y = mTempLocation[1];
             success = true;
-
         }
         markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
         return success;
     }
 
-    // This method looks in the specified direction to see if there are additional views adjacent
-    // to the current set of views. If there are, then these views are added to the current
-    // set of views. This is performed iteratively, giving a cascading push behaviour.
-    private boolean addViewInDirection(ArrayList<View> views, Rect boundingRect, int[] direction,
-            boolean[][] occupied, View dragView, ItemConfiguration currentState) {
-        boolean found = false;
+    /**
+     * This helper class defines a cluster of views. It helps with defining complex edges
+     * of the cluster and determining how those edges interact with other views. The edges
+     * essentially define a fine-grained boundary around the cluster of views -- like a more
+     * precise version of a bounding box.
+     */
+    private class ViewCluster {
+        final static int LEFT = 0;
+        final static int TOP = 1;
+        final static int RIGHT = 2;
+        final static int BOTTOM = 3;
 
-        int childCount = mShortcutsAndWidgets.getChildCount();
-        Rect r0 = new Rect(boundingRect);
-        Rect r1 = new Rect();
+        ArrayList<View> views;
+        ItemConfiguration config;
+        Rect boundingRect = new Rect();
 
-        // First, we consider the rect of the views that we are trying to translate
-        int deltaX = 0;
-        int deltaY = 0;
-        if (direction[1] < 0) {
-            r0.set(r0.left, r0.top - 1, r0.right, r0.bottom - 1);
-            deltaY = -1;
-        } else if (direction[1] > 0) {
-            r0.set(r0.left, r0.top + 1, r0.right, r0.bottom + 1);
-            deltaY = 1;
-        } else if (direction[0] < 0) {
-            r0.set(r0.left - 1, r0.top, r0.right - 1, r0.bottom);
-            deltaX = -1;
-        } else if (direction[0] > 0) {
-            r0.set(r0.left + 1, r0.top, r0.right + 1, r0.bottom);
-            deltaX = 1;
+        int[] leftEdge = new int[mCountY];
+        int[] rightEdge = new int[mCountY];
+        int[] topEdge = new int[mCountX];
+        int[] bottomEdge = new int[mCountX];
+        boolean leftEdgeDirty, rightEdgeDirty, topEdgeDirty, bottomEdgeDirty, boundingRectDirty;
+
+        @SuppressWarnings("unchecked")
+        public ViewCluster(ArrayList<View> views, ItemConfiguration config) {
+            this.views = (ArrayList<View>) views.clone();
+            this.config = config;
+            resetEdges();
         }
 
-        // Now we see which views, if any, are being overlapped by shifting the current group
-        // of views in the desired direction.
-        for (int i = 0; i < childCount; i++) {
-            // We don't need to worry about views already in our group, or the current drag view.
-            View child = mShortcutsAndWidgets.getChildAt(i);
-            if (views.contains(child) || child == dragView) continue;
-            CellAndSpan c = currentState.map.get(child);
+        void resetEdges() {
+            for (int i = 0; i < mCountX; i++) {
+                topEdge[i] = -1;
+                bottomEdge[i] = -1;
+            }
+            for (int i = 0; i < mCountY; i++) {
+                leftEdge[i] = -1;
+                rightEdge[i] = -1;
+            }
+            leftEdgeDirty = true;
+            rightEdgeDirty = true;
+            bottomEdgeDirty = true;
+            topEdgeDirty = true;
+            boundingRectDirty = true;
+        }
 
-            LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            r1.set(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
-            if (Rect.intersects(r0, r1)) {
-                if (!lp.canReorder) {
-                    return false;
-                }
-                // First we verify that the view in question is at the border of the extents
-                // of the block of items we are pushing
-                if ((direction[0] < 0 && c.x == r0.left) ||
-                        (direction[0] > 0 && c.x == r0.right - 1) ||
-                        (direction[1] < 0 && c.y == r0.top) ||
-                        (direction[1] > 0 && c.y == r0.bottom - 1)) {
-                    boolean pushed = false;
-                    // Since the bounding rect is a coarse description of the region (there can
-                    // be holes at the edge of the block), we need to check to verify that a solid
-                    // piece is intersecting. This ensures that interlocking is possible.
-                    for (int x = c.x; x < c.x + c.spanX; x++) {
-                        for (int y = c.y; y < c.y + c.spanY; y++) {
-                            if (occupied[x - deltaX][y - deltaY]) {
-                                pushed = true;
-                                break;
+        void computeEdge(int which, int[] edge) {
+            int count = views.size();
+            for (int i = 0; i < count; i++) {
+                CellAndSpan cs = config.map.get(views.get(i));
+                switch (which) {
+                    case LEFT:
+                        int left = cs.x;
+                        for (int j = cs.y; j < cs.y + cs.spanY; j++) {
+                            if (left < edge[j] || edge[j] < 0) {
+                                edge[j] = left;
                             }
-                            if (pushed) break;
                         }
-                    }
-                    if (pushed) {
-                        views.add(child);
-                        boundingRect.union(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
-                        found = true;
-                    }
+                        break;
+                    case RIGHT:
+                        int right = cs.x + cs.spanX;
+                        for (int j = cs.y; j < cs.y + cs.spanY; j++) {
+                            if (right > edge[j]) {
+                                edge[j] = right;
+                            }
+                        }
+                        break;
+                    case TOP:
+                        int top = cs.y;
+                        for (int j = cs.x; j < cs.x + cs.spanX; j++) {
+                            if (top < edge[j] || edge[j] < 0) {
+                                edge[j] = top;
+                            }
+                        }
+                        break;
+                    case BOTTOM:
+                        int bottom = cs.y + cs.spanY;
+                        for (int j = cs.x; j < cs.x + cs.spanX; j++) {
+                            if (bottom > edge[j]) {
+                                edge[j] = bottom;
+                            }
+                        }
+                        break;
                 }
             }
         }
-        return found;
+
+        boolean isViewTouchingEdge(View v, int whichEdge) {
+            CellAndSpan cs = config.map.get(v);
+
+            int[] edge = getEdge(whichEdge);
+
+            switch (whichEdge) {
+                case LEFT:
+                    for (int i = cs.y; i < cs.y + cs.spanY; i++) {
+                        if (edge[i] == cs.x + cs.spanX) {
+                            return true;
+                        }
+                    }
+                    break;
+                case RIGHT:
+                    for (int i = cs.y; i < cs.y + cs.spanY; i++) {
+                        if (edge[i] == cs.x) {
+                            return true;
+                        }
+                    }
+                    break;
+                case TOP:
+                    for (int i = cs.x; i < cs.x + cs.spanX; i++) {
+                        if (edge[i] == cs.y + cs.spanY) {
+                            return true;
+                        }
+                    }
+                    break;
+                case BOTTOM:
+                    for (int i = cs.x; i < cs.x + cs.spanX; i++) {
+                        if (edge[i] == cs.y) {
+                            return true;
+                        }
+                    }
+                    break;
+            }
+            return false;
+        }
+
+        void shift(int whichEdge, int delta) {
+            for (View v: views) {
+                CellAndSpan c = config.map.get(v);
+                switch (whichEdge) {
+                    case LEFT:
+                        c.x -= delta;
+                        break;
+                    case RIGHT:
+                        c.x += delta;
+                        break;
+                    case TOP:
+                        c.y -= delta;
+                        break;
+                    case BOTTOM:
+                    default:
+                        c.y += delta;
+                        break;
+                }
+            }
+            resetEdges();
+        }
+
+        public void addView(View v) {
+            views.add(v);
+            resetEdges();
+        }
+
+        public Rect getBoundingRect() {
+            if (boundingRectDirty) {
+                boolean first = true;
+                for (View v: views) {
+                    CellAndSpan c = config.map.get(v);
+                    if (first) {
+                        boundingRect.set(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
+                        first = false;
+                    } else {
+                        boundingRect.union(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
+                    }
+                }
+            }
+            return boundingRect;
+        }
+
+        public int[] getEdge(int which) {
+            switch (which) {
+                case LEFT:
+                    return getLeftEdge();
+                case RIGHT:
+                    return getRightEdge();
+                case TOP:
+                    return getTopEdge();
+                case BOTTOM:
+                default:
+                    return getBottomEdge();
+            }
+        }
+
+        public int[] getLeftEdge() {
+            if (leftEdgeDirty) {
+                computeEdge(LEFT, leftEdge);
+            }
+            return leftEdge;
+        }
+
+        public int[] getRightEdge() {
+            if (rightEdgeDirty) {
+                computeEdge(RIGHT, rightEdge);
+            }
+            return rightEdge;
+        }
+
+        public int[] getTopEdge() {
+            if (topEdgeDirty) {
+                computeEdge(TOP, topEdge);
+            }
+            return topEdge;
+        }
+
+        public int[] getBottomEdge() {
+            if (bottomEdgeDirty) {
+                computeEdge(BOTTOM, bottomEdge);
+            }
+            return bottomEdge;
+        }
+
+        PositionComparator comparator = new PositionComparator();
+        class PositionComparator implements Comparator<View> {
+            int whichEdge = 0;
+            public int compare(View left, View right) {
+                CellAndSpan l = config.map.get(left);
+                CellAndSpan r = config.map.get(right);
+                switch (whichEdge) {
+                    case LEFT:
+                        return (r.x + r.spanX) - (l.x + l.spanX);
+                    case RIGHT:
+                        return l.x - r.x;
+                    case TOP:
+                        return (r.y + r.spanY) - (l.y + l.spanY);
+                    case BOTTOM:
+                    default:
+                        return l.y - r.y;
+                }
+            }
+        }
+
+        public void sortConfigurationForEdgePush(int edge) {
+            comparator.whichEdge = edge;
+            Collections.sort(config.sortedViews, comparator);
+        }
     }
 
-    private void completeSetOfViewsToMove(ArrayList<View> views, Rect boundingRect, int[] direction,
-            boolean[][] occupied, View dragView, ItemConfiguration currentState) {
-        Rect r0 = new Rect(boundingRect);
-        int minRuns = 0;
+    private boolean pushViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
+            int[] direction, View dragView, ItemConfiguration currentState) {
 
-        // The first thing we do is to reduce the bounding rect to first or last row or column,
-        // depending on the direction. Then, we add any necessary views that are already contained
-        // by the bounding rect, but aren't in the list of intersecting views, and will be pushed
-        // by something already in the intersecting views.
-        if (direction[1] < 0) {
-            r0.set(r0.left, r0.bottom - 1, r0.right, r0.bottom);
-        } else if (direction[1] > 0) {
-            r0.set(r0.left, r0.top, r0.right, r0.top + 1);
-        } else if (direction[0] < 0) {
-            r0.set(r0.right - 1, r0.top, r0.right, r0.bottom);
+        ViewCluster cluster = new ViewCluster(views, currentState);
+        Rect clusterRect = cluster.getBoundingRect();
+        int whichEdge;
+        int pushDistance;
+        boolean fail = false;
+
+        // Determine the edge of the cluster that will be leading the push and how far
+        // the cluster must be shifted.
+        if (direction[0] < 0) {
+            whichEdge = ViewCluster.LEFT;
+            pushDistance = clusterRect.right - rectOccupiedByPotentialDrop.left;
         } else if (direction[0] > 0) {
-            r0.set(r0.left, r0.top, r0.left + 1, r0.bottom);
+            whichEdge = ViewCluster.RIGHT;
+            pushDistance = rectOccupiedByPotentialDrop.right - clusterRect.left;
+        } else if (direction[1] < 0) {
+            whichEdge = ViewCluster.TOP;
+            pushDistance = clusterRect.bottom - rectOccupiedByPotentialDrop.top;
+        } else {
+            whichEdge = ViewCluster.BOTTOM;
+            pushDistance = rectOccupiedByPotentialDrop.bottom - clusterRect.top;
         }
 
-        minRuns = Math.max(Math.abs(boundingRect.width() - r0.width()),
-                Math.abs(boundingRect.height() - r0.height())) + 1;
-
-        // Here the first number of runs (minRuns) accounts for the the comment above, and
-        // further runs execute based on whether the intersecting views / bounding rect need
-        // to be expanded to include other views that will be pushed.
-        while (addViewInDirection(views, r0, direction, mTmpOccupied,
-                dragView, currentState) || minRuns > 0) {
-            minRuns--;
+        // Break early for invalid push distance.
+        if (pushDistance <= 0) {
+            return false;
         }
-        boundingRect.union(r0);
+
+        // Mark the occupied state as false for the group of views we want to move.
+        for (View v: views) {
+            CellAndSpan c = currentState.map.get(v);
+            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
+        }
+
+        // We save the current configuration -- if we fail to find a solution we will revert
+        // to the initial state. The process of finding a solution modifies the configuration
+        // in place, hence the need for revert in the failure case.
+        currentState.save();
+
+        // The pushing algorithm is simplified by considering the views in the order in which
+        // they would be pushed by the cluster. For example, if the cluster is leading with its
+        // left edge, we consider sort the views by their right edge, from right to left.
+        cluster.sortConfigurationForEdgePush(whichEdge);
+
+        while (pushDistance > 0 && !fail) {
+            for (View v: currentState.sortedViews) {
+                // For each view that isn't in the cluster, we see if the leading edge of the
+                // cluster is contacting the edge of that view. If so, we add that view to the
+                // cluster.
+                if (!cluster.views.contains(v) && v != dragView) {
+                    if (cluster.isViewTouchingEdge(v, whichEdge)) {
+                        LayoutParams lp = (LayoutParams) v.getLayoutParams();
+                        if (!lp.canReorder) {
+                            // The push solution includes the all apps button, this is not viable.
+                            fail = true;
+                            break;
+                        }
+                        cluster.addView(v);
+                        CellAndSpan c = currentState.map.get(v);
+
+                        // Adding view to cluster, mark it as not occupied.
+                        markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
+                    }
+                }
+            }
+            pushDistance--;
+
+            // The cluster has been completed, now we move the whole thing over in the appropriate
+            // direction.
+            cluster.shift(whichEdge, 1);
+        }
+
+        boolean foundSolution = false;
+        clusterRect = cluster.getBoundingRect();
+
+        // Due to the nature of the algorithm, the only check required to verify a valid solution
+        // is to ensure that completed shifted cluster lies completely within the cell layout.
+        if (!fail && clusterRect.left >= 0 && clusterRect.right <= mCountX && clusterRect.top >= 0 &&
+                clusterRect.bottom <= mCountY) {
+            foundSolution = true;
+        } else {
+            currentState.restore();
+        }
+
+        // In either case, we set the occupied array as marked for the location of the views
+        for (View v: cluster.views) {
+            CellAndSpan c = currentState.map.get(v);
+            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
+        }
+
+        return foundSolution;
     }
 
     private boolean addViewsToTempLocation(ArrayList<View> views, Rect rectOccupiedByPotentialDrop,
-            int[] direction, boolean push, View dragView, ItemConfiguration currentState) {
+            int[] direction, View dragView, ItemConfiguration currentState) {
         if (views.size() == 0) return true;
 
         boolean success = false;
@@ -1735,15 +1920,8 @@
             }
         }
 
-        @SuppressWarnings("unchecked")
-        ArrayList<View> dup = (ArrayList<View>) views.clone();
-        if (push) {
-            completeSetOfViewsToMove(dup, boundingRect, direction, mTmpOccupied, dragView,
-                    currentState);
-        }
-
         // Mark the occupied state as false for the group of views we want to move.
-        for (View v: dup) {
+        for (View v: views) {
             CellAndSpan c = currentState.map.get(v);
             markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
         }
@@ -1753,26 +1931,21 @@
         int left = boundingRect.left;
         // We mark more precisely which parts of the bounding rect are truly occupied, allowing
         // for interlocking.
-        for (View v: dup) {
+        for (View v: views) {
             CellAndSpan c = currentState.map.get(v);
             markCellsForView(c.x - left, c.y - top, c.spanX, c.spanY, blockOccupied, true);
         }
 
         markCellsForRect(rectOccupiedByPotentialDrop, mTmpOccupied, true);
 
-        if (push) {
-            findNearestAreaInDirection(boundingRect.left, boundingRect.top, boundingRect.width(),
-                    boundingRect.height(), direction, mTmpOccupied, blockOccupied, mTempLocation);
-        } else {
-            findNearestArea(boundingRect.left, boundingRect.top, boundingRect.width(),
-                    boundingRect.height(), direction, mTmpOccupied, blockOccupied, mTempLocation);
-        }
+        findNearestArea(boundingRect.left, boundingRect.top, boundingRect.width(),
+                boundingRect.height(), direction, mTmpOccupied, blockOccupied, mTempLocation);
 
         // If we successfuly found a location by pushing the block of views, we commit it
         if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
             int deltaX = mTempLocation[0] - boundingRect.left;
             int deltaY = mTempLocation[1] - boundingRect.top;
-            for (View v: dup) {
+            for (View v: views) {
                 CellAndSpan c = currentState.map.get(v);
                 c.x += deltaX;
                 c.y += deltaY;
@@ -1781,7 +1954,7 @@
         }
 
         // In either case, we set the occupied array as marked for the location of the views
-        for (View v: dup) {
+        for (View v: views) {
             CellAndSpan c = currentState.map.get(v);
             markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
         }
@@ -1802,14 +1975,16 @@
             // separately in each of the components.
             int temp = direction[1];
             direction[1] = 0;
-            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
+
+            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                     ignoreView, solution)) {
                 return true;
             }
             direction[1] = temp;
             temp = direction[0];
             direction[0] = 0;
-            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
+
+            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                     ignoreView, solution)) {
                 return true;
             }
@@ -1821,7 +1996,7 @@
             direction[1] *= -1;
             temp = direction[1];
             direction[1] = 0;
-            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
+            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                     ignoreView, solution)) {
                 return true;
             }
@@ -1829,7 +2004,7 @@
             direction[1] = temp;
             temp = direction[0];
             direction[0] = 0;
-            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
+            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                     ignoreView, solution)) {
                 return true;
             }
@@ -1841,15 +2016,14 @@
         } else {
             // If the direction vector has a single non-zero component, we push first in the
             // direction of the vector
-            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
+            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                     ignoreView, solution)) {
                 return true;
             }
-
             // Then we try the opposite direction
             direction[0] *= -1;
             direction[1] *= -1;
-            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
+            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                     ignoreView, solution)) {
                 return true;
             }
@@ -1864,7 +2038,7 @@
             int temp = direction[1];
             direction[1] = direction[0];
             direction[0] = temp;
-            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
+            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                     ignoreView, solution)) {
                 return true;
             }
@@ -1872,7 +2046,7 @@
             // Then we try the opposite direction
             direction[0] *= -1;
             direction[1] *= -1;
-            if (addViewsToTempLocation(intersectingViews, occupied, direction, true,
+            if (pushViewsToTempLocation(intersectingViews, occupied, direction,
                     ignoreView, solution)) {
                 return true;
             }
@@ -1928,7 +2102,7 @@
         }
 
         // Next we try moving the views as a block, but without requiring the push mechanic.
-        if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, false, ignoreView,
+        if (addViewsToTempLocation(mIntersectingViews, mOccupiedRect, direction, ignoreView,
                 solution)) {
             return true;
         }
@@ -2018,7 +2192,7 @@
             } else {
                 c = new CellAndSpan(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan);
             }
-            solution.map.put(child, c);
+            solution.add(child, c);
         }
     }
 
@@ -2490,9 +2664,31 @@
 
     private class ItemConfiguration {
         HashMap<View, CellAndSpan> map = new HashMap<View, CellAndSpan>();
+        private HashMap<View, CellAndSpan> savedMap = new HashMap<View, CellAndSpan>();
+        ArrayList<View> sortedViews = new ArrayList<View>();
         boolean isSolution = false;
         int dragViewX, dragViewY, dragViewSpanX, dragViewSpanY;
 
+        void save() {
+            // Copy current state into savedMap
+            for (View v: map.keySet()) {
+                map.get(v).copy(savedMap.get(v));
+            }
+        }
+
+        void restore() {
+            // Restore current state from savedMap
+            for (View v: savedMap.keySet()) {
+                savedMap.get(v).copy(map.get(v));
+            }
+        }
+
+        void add(View v, CellAndSpan cs) {
+            map.put(v, cs);
+            savedMap.put(v, new CellAndSpan());
+            sortedViews.add(v);
+        }
+
         int area() {
             return dragViewSpanX * dragViewSpanY;
         }
@@ -2502,12 +2698,27 @@
         int x, y;
         int spanX, spanY;
 
+        public CellAndSpan() {
+        }
+
+        public void copy(CellAndSpan copy) {
+            copy.x = x;
+            copy.y = y;
+            copy.spanX = spanX;
+            copy.spanY = spanY;
+        }
+
         public CellAndSpan(int x, int y, int spanX, int spanY) {
             this.x = x;
             this.y = y;
             this.spanX = spanX;
             this.spanY = spanY;
         }
+
+        public String toString() {
+            return "(" + x + ", " + y + ": " + spanX + ", " + spanY + ")";
+        }
+
     }
 
     /**
diff --git a/src/com/android/launcher2/Launcher.java b/src/com/android/launcher2/Launcher.java
index c221815..a4ed91f 100644
--- a/src/com/android/launcher2/Launcher.java
+++ b/src/com/android/launcher2/Launcher.java
@@ -50,8 +50,10 @@
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -99,7 +101,6 @@
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -146,8 +147,10 @@
     static final int DEFAULT_SCREEN = 2;
 
     private static final String PREFERENCES = "launcher.preferences";
-    static final String FORCE_ENABLE_ROTATION_PROPERTY = "debug.force_enable_rotation";
-    static final String DUMP_STATE_PROPERTY = "debug.dumpstate";
+    // To turn on these properties, type
+    // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
+    static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate";
+    static final String DUMP_STATE_PROPERTY = "launcher_dump_state";
 
     // The Intent extra that defines whether to ignore the launch animation
     static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
@@ -209,6 +212,7 @@
     private Workspace mWorkspace;
     private View mQsbDivider;
     private View mDockDivider;
+    private View mLauncherView;
     private DragLayer mDragLayer;
     private DragController mDragController;
 
@@ -280,6 +284,9 @@
     private static Drawable.ConstantState[] sVoiceSearchIcon = new Drawable.ConstantState[2];
     private static Drawable.ConstantState[] sAppMarketIcon = new Drawable.ConstantState[2];
 
+    private Drawable mWorkspaceBackgroundDrawable;
+    private Drawable mBlackBackgroundDrawable;
+
     private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
 
     static final ArrayList<String> sDumpLogs = new ArrayList<String>();
@@ -313,6 +320,8 @@
     private static ArrayList<PendingAddArguments> sPendingAddList
             = new ArrayList<PendingAddArguments>();
 
+    private static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY);
+
     private static class PendingAddArguments {
         int requestCode;
         Intent intent;
@@ -322,18 +331,8 @@
         int cellY;
     }
 
-
-    private boolean doesFileExist(String filename) {
-        FileInputStream fis = null;
-        try {
-            fis = openFileInput(filename);
-            fis.close();
-            return true;
-        } catch (java.io.FileNotFoundException e) {
-            return false;
-        } catch (java.io.IOException e) {
-            return true;
-        }
+    private static boolean isPropertyEnabled(String propertyName) {
+        return Log.isLoggable(propertyName, Log.VERBOSE);
     }
 
     @Override
@@ -727,6 +726,9 @@
         }
         mOnResumeState = State.NONE;
 
+        // Background was set to gradient in onPause(), restore to black if in all apps.
+        setWorkspaceBackground(mState == State.WORKSPACE);
+
         // Process any items that were added while Launcher was away
         InstallShortcutReceiver.flushInstallQueue(this);
 
@@ -928,10 +930,15 @@
     private void setupViews() {
         final DragController dragController = mDragController;
 
+        mLauncherView = findViewById(R.id.launcher);
         mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
         mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
-        mQsbDivider = (ImageView) findViewById(R.id.qsb_divider);
-        mDockDivider = (ImageView) findViewById(R.id.dock_divider);
+        mQsbDivider = findViewById(R.id.qsb_divider);
+        mDockDivider = findViewById(R.id.dock_divider);
+
+        mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+        mWorkspaceBackgroundDrawable = getResources().getDrawable(R.drawable.workspace_bg);
+        mBlackBackgroundDrawable = new ColorDrawable(Color.BLACK);
 
         // Setup the drag layer
         mDragLayer.setup(this, dragController);
@@ -952,12 +959,11 @@
         mSearchDropTargetBar = (SearchDropTargetBar) mDragLayer.findViewById(R.id.qsb_bar);
 
         // Setup AppsCustomize
-        mAppsCustomizeTabHost = (AppsCustomizeTabHost)
-                findViewById(R.id.apps_customize_pane);
+        mAppsCustomizeTabHost = (AppsCustomizeTabHost) findViewById(R.id.apps_customize_pane);
         mAppsCustomizeContent = (AppsCustomizePagedView)
                 mAppsCustomizeTabHost.findViewById(R.id.apps_customize_pane_content);
         mAppsCustomizeContent.setup(this, dragController);
-        
+
         // Setup the drag controller (drop targets have to be added in reverse order in priority)
         dragController.setDragScoller(mWorkspace);
         dragController.setScrollView(mDragLayer);
@@ -1390,6 +1396,10 @@
 
             Runnable processIntent = new Runnable() {
                 public void run() {
+                    if (mWorkspace == null) {
+                        // Can be cases where mWorkspace is null, this prevents a NPE
+                        return;
+                    }
                     Folder openFolder = mWorkspace.getOpenFolder();
                     // In all these cases, only animate if we're already on home
                     mWorkspace.exitWidgetResizeMode();
@@ -1866,7 +1876,7 @@
                 case KeyEvent.KEYCODE_HOME:
                     return true;
                 case KeyEvent.KEYCODE_VOLUME_DOWN:
-                    if (doesFileExist(DUMP_STATE_PROPERTY)) {
+                    if (isPropertyEnabled(DUMP_STATE_PROPERTY)) {
                         dumpState();
                         return true;
                     }
@@ -2351,10 +2361,12 @@
     }
 
     // Now a part of LauncherModel.Callbacks. Used to reorder loading steps.
+    @Override
     public boolean isAllAppsVisible() {
         return (mState == State.APPS_CUSTOMIZE) || (mOnResumeState == State.APPS_CUSTOMIZE);
     }
 
+    @Override
     public boolean isAllAppsButtonRank(int rank) {
         return mHotseat.isAllAppsButtonRank(rank);
     }
@@ -2362,7 +2374,6 @@
     /**
      * Helper method for the cameraZoomIn/cameraZoomOut animations
      * @param view The view being animated
-     * @param state The state that we are moving in or out of (eg. APPS_CUSTOMIZE)
      * @param scaleFactor The scale factor used for the zoom
      */
     private void setPivotsForZoom(View view, float scaleFactor) {
@@ -2380,6 +2391,11 @@
         }
     }
 
+    private void setWorkspaceBackground(boolean workspace) {
+        mLauncherView.setBackground(workspace ?
+                mWorkspaceBackgroundDrawable : mBlackBackgroundDrawable);
+    }
+
     void updateWallpaperVisibility(boolean visible) {
         int wpflags = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
         int curflags = getWindow().getAttributes().flags
@@ -2387,6 +2403,7 @@
         if (wpflags != curflags) {
             getWindow().setFlags(wpflags, WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
         }
+        setWorkspaceBackground(visible);
     }
 
     private void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
@@ -3658,8 +3675,7 @@
     }
 
     public boolean isRotationEnabled() {
-        boolean forceEnableRotation = doesFileExist(FORCE_ENABLE_ROTATION_PROPERTY);
-        boolean enableRotation = forceEnableRotation ||
+        boolean enableRotation = sForceEnableRotation ||
                 getResources().getBoolean(R.bool.allow_rotation);
         return enableRotation;
     }
@@ -3723,7 +3739,9 @@
     }
 
     private void dismissCling(final Cling cling, final String flag, int duration) {
-        if (cling != null && cling.getVisibility() == View.VISIBLE) {
+        // To catch cases where siblings of top-level views are made invisible, just check whether
+        // the cling is directly set to GONE before dismissing it.
+        if (cling != null && cling.getVisibility() != View.GONE) {
             ObjectAnimator anim = LauncherAnimUtils.ofFloat(cling, "alpha", 0f);
             anim.setDuration(duration);
             anim.addListener(new AnimatorListenerAdapter() {
diff --git a/src/com/android/launcher2/LauncherAppWidgetInfo.java b/src/com/android/launcher2/LauncherAppWidgetInfo.java
index 602695a..f001b2b 100644
--- a/src/com/android/launcher2/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher2/LauncherAppWidgetInfo.java
@@ -19,7 +19,6 @@
 import android.appwidget.AppWidgetHostView;
 import android.content.ComponentName;
 import android.content.ContentValues;
-import android.os.Build;
 
 /**
  * Represents a widget (either instantiated or about to be) in the Launcher.
diff --git a/src/com/android/launcher2/LauncherModel.java b/src/com/android/launcher2/LauncherModel.java
index 7302310..ccf8328 100644
--- a/src/com/android/launcher2/LauncherModel.java
+++ b/src/com/android/launcher2/LauncherModel.java
@@ -578,8 +578,6 @@
         values.put(LauncherSettings.Favorites._ID, item.id);
         item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
 
-        final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
-
         Runnable r = new Runnable() {
             public void run() {
                 String transaction = "DbDebug    Add item (" + item.title + ") to db, id: "
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index 2cb943d..6d5d151 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -297,9 +297,13 @@
      * the previous tab page.
      */
     protected void updateCurrentPageScroll() {
-        int offset = getChildOffset(mCurrentPage);
-        int relOffset = getRelativeChildOffset(mCurrentPage);
-        int newX = offset - relOffset;
+        // If the current page is invalid, just reset the scroll position to zero
+        int newX = 0;
+        if (0 <= mCurrentPage && mCurrentPage < getPageCount()) {
+            int offset = getChildOffset(mCurrentPage);
+            int relOffset = getRelativeChildOffset(mCurrentPage);
+            newX = offset - relOffset;
+        }
         scrollTo(newX, 0);
         mScroller.setFinalX(newX);
         mScroller.forceFinished(true);
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 4940ae0..2c44aca 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -39,7 +39,6 @@
 import android.graphics.Rect;
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.os.IBinder;
 import android.os.Parcelable;
 import android.util.AttributeSet;