am 29cff2d2: (-s ours) Import translations. DO NOT MERGE

* commit '29cff2d21aae2dbde622f99240cfe4d19fbc5e4a':
  Import translations. DO NOT MERGE
diff --git a/res/drawable-sw600dp-hdpi/homescreen_blue_normal_holo.9.png b/res/drawable-sw600dp-hdpi/homescreen_blue_normal_holo.9.png
deleted file mode 100644
index 98f7ac8..0000000
--- a/res/drawable-sw600dp-hdpi/homescreen_blue_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/homescreen_blue_strong_holo.9.png b/res/drawable-sw600dp-hdpi/homescreen_blue_strong_holo.9.png
deleted file mode 100644
index 0a511f9..0000000
--- a/res/drawable-sw600dp-hdpi/homescreen_blue_strong_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/ic_allapps.png b/res/drawable-sw600dp-hdpi/ic_allapps.png
deleted file mode 100644
index 8bda435..0000000
--- a/res/drawable-sw600dp-hdpi/ic_allapps.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/ic_allapps_pressed.png b/res/drawable-sw600dp-hdpi/ic_allapps_pressed.png
deleted file mode 100644
index 07ff331..0000000
--- a/res/drawable-sw600dp-hdpi/ic_allapps_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/overscroll_glow_left.9.png b/res/drawable-sw600dp-hdpi/overscroll_glow_left.9.png
deleted file mode 100644
index 3928e2c..0000000
--- a/res/drawable-sw600dp-hdpi/overscroll_glow_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/overscroll_glow_right.9.png b/res/drawable-sw600dp-hdpi/overscroll_glow_right.9.png
deleted file mode 100644
index e34de34..0000000
--- a/res/drawable-sw600dp-hdpi/overscroll_glow_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/portal_ring_inner_holo.png b/res/drawable-sw600dp-hdpi/portal_ring_inner_holo.png
deleted file mode 100644
index b3be472..0000000
--- a/res/drawable-sw600dp-hdpi/portal_ring_inner_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/portal_ring_inner_nolip_holo.png b/res/drawable-sw600dp-hdpi/portal_ring_inner_nolip_holo.png
deleted file mode 100644
index 96b981c..0000000
--- a/res/drawable-sw600dp-hdpi/portal_ring_inner_nolip_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/portal_ring_outer_holo.png b/res/drawable-sw600dp-hdpi/portal_ring_outer_holo.png
deleted file mode 100644
index bc13a26..0000000
--- a/res/drawable-sw600dp-hdpi/portal_ring_outer_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/homescreen_blue_normal_holo.9.png b/res/drawable-sw600dp-mdpi/homescreen_blue_normal_holo.9.png
deleted file mode 100644
index 3ae4aff..0000000
--- a/res/drawable-sw600dp-mdpi/homescreen_blue_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/homescreen_blue_strong_holo.9.png b/res/drawable-sw600dp-mdpi/homescreen_blue_strong_holo.9.png
deleted file mode 100644
index 31148dd..0000000
--- a/res/drawable-sw600dp-mdpi/homescreen_blue_strong_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/ic_allapps.png b/res/drawable-sw600dp-mdpi/ic_allapps.png
deleted file mode 100644
index e2afea5..0000000
--- a/res/drawable-sw600dp-mdpi/ic_allapps.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/ic_allapps_pressed.png b/res/drawable-sw600dp-mdpi/ic_allapps_pressed.png
deleted file mode 100644
index d409c7e..0000000
--- a/res/drawable-sw600dp-mdpi/ic_allapps_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/overscroll_glow_left.9.png b/res/drawable-sw600dp-mdpi/overscroll_glow_left.9.png
deleted file mode 100644
index 58ec10c..0000000
--- a/res/drawable-sw600dp-mdpi/overscroll_glow_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/overscroll_glow_right.9.png b/res/drawable-sw600dp-mdpi/overscroll_glow_right.9.png
deleted file mode 100644
index a986fd3..0000000
--- a/res/drawable-sw600dp-mdpi/overscroll_glow_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/portal_ring_inner_holo.png b/res/drawable-sw600dp-mdpi/portal_ring_inner_holo.png
deleted file mode 100644
index 319c074..0000000
--- a/res/drawable-sw600dp-mdpi/portal_ring_inner_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/portal_ring_inner_nolip_holo.png b/res/drawable-sw600dp-mdpi/portal_ring_inner_nolip_holo.png
deleted file mode 100644
index 8537714..0000000
--- a/res/drawable-sw600dp-mdpi/portal_ring_inner_nolip_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/portal_ring_outer_holo.png b/res/drawable-sw600dp-mdpi/portal_ring_outer_holo.png
deleted file mode 100644
index 365dcfc..0000000
--- a/res/drawable-sw600dp-mdpi/portal_ring_outer_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/homescreen_blue_normal_holo.9.png b/res/drawable-sw600dp-xhdpi/homescreen_blue_normal_holo.9.png
deleted file mode 100644
index c25b590..0000000
--- a/res/drawable-sw600dp-xhdpi/homescreen_blue_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/homescreen_blue_strong_holo.9.png b/res/drawable-sw600dp-xhdpi/homescreen_blue_strong_holo.9.png
deleted file mode 100644
index 24ac880..0000000
--- a/res/drawable-sw600dp-xhdpi/homescreen_blue_strong_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/ic_allapps.png b/res/drawable-sw600dp-xhdpi/ic_allapps.png
deleted file mode 100644
index 8fed290..0000000
--- a/res/drawable-sw600dp-xhdpi/ic_allapps.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/ic_allapps_pressed.png b/res/drawable-sw600dp-xhdpi/ic_allapps_pressed.png
deleted file mode 100644
index 786b676..0000000
--- a/res/drawable-sw600dp-xhdpi/ic_allapps_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/overscroll_glow_left.9.png b/res/drawable-sw600dp-xhdpi/overscroll_glow_left.9.png
deleted file mode 100644
index b66dd2f..0000000
--- a/res/drawable-sw600dp-xhdpi/overscroll_glow_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/overscroll_glow_right.9.png b/res/drawable-sw600dp-xhdpi/overscroll_glow_right.9.png
deleted file mode 100644
index 3ccce33..0000000
--- a/res/drawable-sw600dp-xhdpi/overscroll_glow_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/portal_ring_inner_holo.png b/res/drawable-sw600dp-xhdpi/portal_ring_inner_holo.png
deleted file mode 100644
index d4ce45f..0000000
--- a/res/drawable-sw600dp-xhdpi/portal_ring_inner_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/portal_ring_inner_nolip_holo.png b/res/drawable-sw600dp-xhdpi/portal_ring_inner_nolip_holo.png
deleted file mode 100644
index 9aa13c9..0000000
--- a/res/drawable-sw600dp-xhdpi/portal_ring_inner_nolip_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/portal_ring_outer_holo.png b/res/drawable-sw600dp-xhdpi/portal_ring_outer_holo.png
deleted file mode 100644
index 0106cd6..0000000
--- a/res/drawable-sw600dp-xhdpi/portal_ring_outer_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/homescreen_blue_normal_holo.9.png b/res/drawable-sw600dp-xxhdpi/homescreen_blue_normal_holo.9.png
deleted file mode 100644
index c661f68..0000000
--- a/res/drawable-sw600dp-xxhdpi/homescreen_blue_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/homescreen_blue_strong_holo.9.png b/res/drawable-sw600dp-xxhdpi/homescreen_blue_strong_holo.9.png
deleted file mode 100644
index bf6ab97..0000000
--- a/res/drawable-sw600dp-xxhdpi/homescreen_blue_strong_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/ic_allapps.png b/res/drawable-sw600dp-xxhdpi/ic_allapps.png
deleted file mode 100644
index 2429656..0000000
--- a/res/drawable-sw600dp-xxhdpi/ic_allapps.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/ic_allapps_pressed.png b/res/drawable-sw600dp-xxhdpi/ic_allapps_pressed.png
deleted file mode 100644
index b93a51b..0000000
--- a/res/drawable-sw600dp-xxhdpi/ic_allapps_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/overscroll_glow_left.9.png b/res/drawable-sw600dp-xxhdpi/overscroll_glow_left.9.png
deleted file mode 100644
index 472c3f9..0000000
--- a/res/drawable-sw600dp-xxhdpi/overscroll_glow_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/overscroll_glow_right.9.png b/res/drawable-sw600dp-xxhdpi/overscroll_glow_right.9.png
deleted file mode 100644
index e30cc97..0000000
--- a/res/drawable-sw600dp-xxhdpi/overscroll_glow_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/portal_ring_inner_holo.png b/res/drawable-sw600dp-xxhdpi/portal_ring_inner_holo.png
deleted file mode 100644
index 65b2541..0000000
--- a/res/drawable-sw600dp-xxhdpi/portal_ring_inner_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/portal_ring_inner_nolip_holo.png b/res/drawable-sw600dp-xxhdpi/portal_ring_inner_nolip_holo.png
deleted file mode 100644
index 5068646..0000000
--- a/res/drawable-sw600dp-xxhdpi/portal_ring_inner_nolip_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/portal_ring_outer_holo.png b/res/drawable-sw600dp-xxhdpi/portal_ring_outer_holo.png
deleted file mode 100644
index 6628425..0000000
--- a/res/drawable-sw600dp-xxhdpi/portal_ring_outer_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw720dp-hdpi/workspace_bg.9.png b/res/drawable-sw720dp-hdpi/workspace_bg.9.png
deleted file mode 100644
index 5bbfa4f..0000000
--- a/res/drawable-sw720dp-hdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw720dp-mdpi/workspace_bg.9.png b/res/drawable-sw720dp-mdpi/workspace_bg.9.png
deleted file mode 100644
index 2856e09..0000000
--- a/res/drawable-sw720dp-mdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw720dp-xhdpi/workspace_bg.9.png b/res/drawable-sw720dp-xhdpi/workspace_bg.9.png
deleted file mode 100644
index 72269f2..0000000
--- a/res/drawable-sw720dp-xhdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/layout-land/all_apps_cling.xml b/res/layout-land/all_apps_cling.xml
deleted file mode 100644
index 820f00a..0000000
--- a/res/layout-land/all_apps_cling.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.launcher3.Cling
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
-    launcher:drawIdentifier="all_apps_landscape">
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginStart="40dp"
-        android:layout_marginTop="40dp">
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical">
-            <TextView
-                style="@style/ClingTitleText"
-                android:id="@+id/all_apps_cling_title"
-                android:text="@string/all_apps_cling_title" />
-            <TextView
-                style="@style/ClingText"
-                android:id="@+id/all_apps_cling_add_item"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/all_apps_cling_add_item" />
-        </LinearLayout>
-    </FrameLayout>
-    <Button
-        style="@style/ClingButton"
-        android:id="@+id/cling_dismiss"
-        android:layout_marginBottom="15dp"
-        android:layout_marginEnd="10dp"
-        android:layout_gravity="bottom|end"
-        android:onClick="dismissAllAppsCling" />
-</com.android.launcher3.Cling>
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 77ea2e9..5874c80 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -37,7 +37,6 @@
             android:layout_height="match_parent"
             android:layout_gravity="center"
             launcher:defaultScreen="@integer/config_workspaceDefaultScreen"
-            launcher:pageSpacing="@dimen/workspace_page_spacing"
             launcher:pageIndicator="@id/page_indicator" />
 
         <include layout="@layout/hotseat"
diff --git a/res/layout-port/all_apps_cling.xml b/res/layout-port/all_apps_cling.xml
deleted file mode 100644
index 62284ec..0000000
--- a/res/layout-port/all_apps_cling.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.launcher3.Cling
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
-    launcher:drawIdentifier="all_apps_portrait">
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginStart="20dp"
-        android:layout_marginTop="20dp">
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical">
-            <TextView
-                style="@style/ClingTitleText"
-                android:id="@+id/all_apps_cling_title"
-                android:text="@string/all_apps_cling_title" />
-            <TextView
-                style="@style/ClingText"
-                android:id="@+id/all_apps_cling_add_item"
-                android:layout_width="285dp"
-                android:layout_height="wrap_content"
-                android:text="@string/all_apps_cling_add_item" />
-        </LinearLayout>
-    </FrameLayout>
-    <Button
-        style="@style/ClingButton"
-        android:id="@+id/cling_dismiss"
-        android:layout_marginBottom="15dp"
-        android:layout_marginEnd="10dp"
-        android:layout_gravity="bottom|end"
-        android:onClick="dismissAllAppsCling" />
-</com.android.launcher3.Cling>
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 6fbf7c7..49e6b6a 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -35,7 +35,6 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             launcher:defaultScreen="@integer/config_workspaceDefaultScreen"
-            launcher:pageSpacing="@dimen/workspace_page_spacing"
             launcher:pageIndicator="@id/page_indicator">
         </com.android.launcher3.Workspace>
 
diff --git a/res/layout-sw600dp-port/all_apps_cling.xml b/res/layout-sw600dp-port/all_apps_cling.xml
deleted file mode 100644
index cf65e41..0000000
--- a/res/layout-sw600dp-port/all_apps_cling.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.launcher3.Cling
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
-    launcher:drawIdentifier="all_apps_portrait">
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginStart="20dp"
-        android:layout_marginTop="20dp">
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical">
-            <TextView
-                style="@style/ClingTitleText"
-                android:id="@+id/all_apps_cling_title"
-                android:text="@string/all_apps_cling_title" />
-            <TextView
-                style="@style/ClingText"
-                android:id="@+id/all_apps_cling_add_item"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/all_apps_cling_add_item" />
-        </LinearLayout>
-    </FrameLayout>
-    <Button
-        style="@style/ClingButton"
-        android:id="@+id/cling_dismiss"
-        android:minWidth="168dp"
-        android:textSize="24sp"
-        android:layout_marginTop="235dp"
-        android:layout_marginEnd="36dp"
-        android:layout_gravity="top|end"
-        android:onClick="dismissAllAppsCling" />
-</com.android.launcher3.Cling>
diff --git a/res/layout-sw720dp/all_apps_cling.xml b/res/layout-sw720dp/all_apps_cling.xml
deleted file mode 100644
index 824d84f..0000000
--- a/res/layout-sw720dp/all_apps_cling.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.launcher3.Cling
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
-    launcher:drawIdentifier="all_apps_large">
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginStart="@dimen/cling_text_block_offset_x"
-        android:layout_marginTop="@dimen/cling_text_block_offset_y">
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:orientation="vertical">
-            <TextView
-                style="@style/ClingTitleText"
-                android:id="@+id/all_apps_cling_title"
-                android:text="@string/all_apps_cling_title" />
-            <TextView
-                style="@style/ClingText"
-                android:id="@+id/all_apps_cling_add_item"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/all_apps_cling_add_item" />
-            <Button
-                style="@style/ClingButton"
-                android:id="@+id/cling_dismiss"
-                android:layout_marginTop="15dp"
-                android:onClick="dismissAllAppsCling" />
-        </LinearLayout>
-    </FrameLayout>
-</com.android.launcher3.Cling>
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 951e63a..7dac271 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -36,7 +36,6 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             launcher:defaultScreen="@integer/config_workspaceDefaultScreen"
-            launcher:pageSpacing="@dimen/workspace_page_spacing"
             launcher:pageIndicator="@id/page_indicator">
         </com.android.launcher3.Workspace>
 
diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml
index 4405682..5147f99 100644
--- a/res/layout/folder_icon.xml
+++ b/res/layout/folder_icon.xml
@@ -30,5 +30,8 @@
         android:src="@drawable/portal_ring_inner_holo"/>
     <com.android.launcher3.BubbleTextView
         style="@style/WorkspaceIcon"
-        android:id="@+id/folder_icon_name" />
+        android:id="@+id/folder_icon_name"
+        android:layout_gravity="top"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
 </com.android.launcher3.FolderIcon>
diff --git a/res/layout/wallpaper_cropper.xml b/res/layout/wallpaper_cropper.xml
index 3a3d98a..abb8608 100644
--- a/res/layout/wallpaper_cropper.xml
+++ b/res/layout/wallpaper_cropper.xml
@@ -32,7 +32,7 @@
         android:visibility="invisible"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center"
+        android:layout_centerInParent="true"
         android:indeterminate="true"
         android:indeterminateOnly="true"
         android:background="@android:color/transparent" />
diff --git a/res/layout/wallpaper_picker.xml b/res/layout/wallpaper_picker.xml
index c91cc7e..c36493d 100644
--- a/res/layout/wallpaper_picker.xml
+++ b/res/layout/wallpaper_picker.xml
@@ -33,7 +33,7 @@
         android:visibility="invisible"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center"
+        android:layout_centerInParent="true"
         android:indeterminate="true"
         android:indeterminateOnly="true"
         android:background="@android:color/transparent" />
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index e167189..a079a74 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Android-kernprogramme"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Stel muurpapier"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Kon nie prent laai nie"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Kon nie prent as muurpapier laai nie"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d gekies"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d gekies"</item>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 6e8aaa4..161597d 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Android ዋና መተግበሪያዎች"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"የግድግዳ ወረቀት አዘጋጅ"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"ምስሉን መጫን አልተቻለም"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"ምስሉን እንደ የግድግዳ ወረቀት መጫን አልተቻለም"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d ተመርጧል"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d ተመርጧል"</item>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 4794772..ec60179 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"‏تطبيقات Android الأساسية"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"تعيين خلفية"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"تعذر تحميل الصورة"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"تعذر تحميل الصورة كخلفية"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"‏تم تحديد %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"‏تم تحديد %1$d"</item>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index ddcf404..4aa90d4 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -28,6 +28,10 @@
     <string name="folder_name" msgid="7371454440695724752"></string>
     <!-- no translation found for wallpaper_instructions (563973358787555519) -->
     <skip />
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
     <!-- no translation found for number_of_items_selected:zero (7464587177007785408) -->
     <!-- no translation found for number_of_items_selected:one (142482526010824029) -->
     <!-- no translation found for number_of_items_selected:other (1418352074806573570) -->
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 2f53fa3..b56449e 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Основни приложения на Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Задаване на тапета"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Изображението не можа да бъде заредено"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Изображението не можа да бъде заредено като тапет"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Избрахте %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Избрахте %1$d"</item>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 3400482..2623899 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Aplicacions principals d\'Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Estableix el fons de pantalla"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Seleccionats: %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Seleccionats: %1$d"</item>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 1061e79..f2a6634 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Nastavit jako tapetu"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Vybráno: %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Vybráno: %1$d"</item>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 5db246e..dad134d 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Kerneapps i Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Angiv baggrund"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Billedet kunne ikke indlæses"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Billedet kunne ikke indlæses som baggrund"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d er valgt"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d er valgt"</item>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 7a5e7e6..bff456b 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Hintergrund auswählen"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Bild konnte nicht geladen werden."</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Bild konnte nicht als Hintergrund geladen werden."</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d ausgewählt"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d ausgewählt"</item>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 74322e0..e3c8397 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Βασικές εφαρμογές Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Ορισμός ταπετσαρίας"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Δεν ήταν δυνατή η φόρτωση της εικόνας"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Δεν ήταν δυνατή η φόρτωση της εικόνας ως ταπετσαρία"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d επιλεγμένα"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d επιλεγμένα"</item>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 316585e..2f7f39a 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Set wallpaper"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Coudn\'t load image"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Couldn\'t load image as wallpaper"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d selected"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d selected"</item>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 316585e..2f7f39a 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Set wallpaper"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Coudn\'t load image"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Couldn\'t load image as wallpaper"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d selected"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d selected"</item>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index c9a44a9..e09cf6c 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Aplicaciones básicas de Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Establecer como fondo de pantalla"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d seleccionados"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d seleccionado"</item>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 193e096..6c0449d 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Aplicaciones básicas de Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Establecer fondo"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"No se ha podido cargar la imagen"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"No se ha podido cargar la imagen como fondo de pantalla"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Seleccionados: %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Seleccionados: %1$d"</item>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index f026b08..a96120a 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Androidi tuumrakendused"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Määra taustapilt"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Kujutist ei õnnestunud laadida"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Kujutist ei õnnestunud taustapildina laadida"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Valitud on %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Valitud on %1$d"</item>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 657b5db..c3574ae 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"‏برنامه‌های Android Core"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"تنظیم کاغذدیواری"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"بارگیری تصویر انجام نشد"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"بارگیری تصویر به‌عنوان کاغذدیواری انجام نشد"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"‏%1$d انتخاب شد"</item>
     <item quantity="one" msgid="142482526010824029">"‏%1$d انتخاب شد"</item>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index d327250..9c95b81 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Androidin ydinsovellukset"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Aseta taustakuva"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Kuvan lataus epäonnistui"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Kuvaa ei voitu ladata taustakuvaksi"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d valittu"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d valittu"</item>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 9d8dbeb..c55c756 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Applications de base Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Définir le fond d\'écran"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d sélectionné"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d sélectionné"</item>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index b1b0a79..b711b3f 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Applications de base Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Définir comme fond d\'écran"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Impossible de charger l\'image."</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Impossible de charger l\'image comme fond d\'écran."</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d fond d\'écran sélectionné"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d fond d\'écran sélectionné"</item>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index eddd75c..fa2de49 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Android के मुख्य एप्लिकेशन"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"वॉलपेपर सेट करें"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"चित्र लोड नहीं किया जा सका"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"चित्र को वॉलपेपर के रूप में लोड नहीं किया जा सका"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d चयनित"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d चयनित"</item>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 783946d..be85df9 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Matične aplikacije za Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Postavi pozadinsku sliku"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Odabrano: %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Odabrano: %1$d"</item>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 8876652..70b83e9 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Alap Android-alkalmazások"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Háttérkép beállítása"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"A kép betöltése nem sikerült"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"A kép betöltése háttérképként nem sikerült"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d kiválasztva"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d kiválasztva"</item>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index ab7388a..a07c985 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Սահմանել պաստառը"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Չհաջողվեց բեռնել նկարը"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Չհաջողվեց նկարը սահմանել որպես պաստառ"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d ընտրված"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d ընտրված"</item>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index e1e93f7..c03b685 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Aplikasi Inti Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Setel wallpaper"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d dipilih"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d dipilih"</item>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index d5ada8f..e5b76e0 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Applicazioni di base Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Imposta sfondo"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Impossibile caricare l\'immagine"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Impossibile caricare l\'immagine come sfondo"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d selezionati"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d selezionato"</item>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 562ce4f..cb58ffc 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"‏אפליקציות הליבה של Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"בחר טפט"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"לא ניתן היה לטעון את התמונה"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"לא ניתן היה לטעון את התמונה כטפט"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"‏%1$d נבחרו"</item>
     <item quantity="one" msgid="142482526010824029">"‏%1$d נבחרו"</item>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 6e39c54..563e3b6 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"壁紙を設定"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d件選択済み"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d件選択済み"</item>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index a2e3374..806504b 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Android-ის ბირთვის აპები"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"ფონის დაყენება"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"არჩეულია %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"არჩეულია %1$d"</item>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index 977b2dd..8a23c54 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"កម្មវិធី​​សំខាន់​ៗ​របស់ Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"កំណត់​ផ្ទាំង​រូបភាព"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"មិន​អាច​ផ្ទុក​រូបភាព"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"មិន​អាច​ផ្ទុក​រូបភាព​ជា​ផ្ទាំង​រូបភាព"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"បាន​ជ្រើស %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"បាន​ជ្រើស %1$d"</item>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 31c2d69..574d1ae 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Android 핵심 앱"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"배경화면 설정"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d개 선택됨"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d개 선택됨"</item>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 5961b19..07d9279 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -19,11 +19,6 @@
     <dimen name="toolbar_button_vertical_padding">8dip</dimen>
     <dimen name="toolbar_button_horizontal_padding">0dip</dimen>
 
-<!-- Workspace -->
-    <!-- We really want the page spacing to be the max of either the button bar
-     height or the qsb bar height -->
-    <dimen name="workspace_page_spacing">-1dp</dimen>
-
 <!-- AppsCustomize -->
     <dimen name="apps_customize_tab_bar_height">42dp</dimen>
     <integer name="apps_customize_widget_cell_count_x">3</integer>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index e3a95dd..8215d7b 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"ແອັບພລິເຄຊັນຫຼັກຂອງ Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"ຕັ້ງເປັນພາບພື້ນຫຼັງ"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"ບໍ່ສາມາດໂຫຼດຮູບພາບໄດ້"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"ບໍ່​ສາ​ມາດ​ໂຫຼດ​ຮູບພາບ​ເປັນ​ຮູບພື້ນຫຼັງໄດ້"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"ເລືອກ %1$d ລາຍການແລ້ວ"</item>
     <item quantity="one" msgid="142482526010824029">"ເລືອກ %1$d ລາຍການແລ້ວ"</item>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 80d4412..e7c78ce 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Pagrindinės „Android“ programos"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Nustatyti ekrano foną"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Nepavyko įkelti vaizdo"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Nepavyko įkelti vaizdo kaip ekrano fono"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Pasirinkta: %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Pasirinkta: %1$d"</item>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index b70f5dc..8af03ca 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Android pamatlietotnes"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Iestatīt fona tapeti"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Nevarēja ielādēt attēlu."</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Nevarēja ielādēt attēlu kā fona tapeti."</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Atlasīti: %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Atlasīti: %1$d"</item>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index 7cf7a9c..d30c3fa 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Андройд үндсэн апп"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Ханын зургийг тохируулах"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d сонгогдсон"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d сонгогдсон"</item>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 4ee3545..7658735 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Apl Teras Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Tetapkan kertas dinding"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Tidak dapat memuatkan imej"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Tidak dapat memuatkan imej sebagai kertas dinding"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d dipilih"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d dipilih"</item>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index ce50754..d00c846 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Kjerneapper for Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Angi bakgrunn"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Kunne ikke laste inn bildet"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Kunne ikke laste inn bildet som bakgrunn"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d valgt"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d valgt"</item>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index e2a2a07..a47b0e1 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Android-kernapps"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Achtergrond instellen"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d geselecteerd"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d geselecteerd"</item>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index b629f3d..b920993 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Główne aplikacje Androida"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Ustaw tapetę"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Nie udało się załadować obrazu"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Nie udało się załadować obrazu jako tapety"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Wybrane: %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Wybrane: %1$d"</item>
diff --git a/res/values-port/dimens.xml b/res/values-port/dimens.xml
index 7194a2a..7753ab3 100644
--- a/res/values-port/dimens.xml
+++ b/res/values-port/dimens.xml
@@ -15,9 +15,6 @@
 -->
 
 <resources>
-<!-- Workspace -->
-    <dimen name="workspace_page_spacing">-1dp</dimen>
-
 <!-- AppsCustomize -->
     <integer name="apps_customize_cling_focused_x">1</integer>
     <integer name="apps_customize_cling_focused_y">1</integer>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 1ef3982..fb5cca9 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Aplicações principais do Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Definir imagem fundo"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Não foi possível carregar a imagem"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Não foi possível carregar a imagem como imagem de fundo"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d selecionado(s)"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d selecionado(s)"</item>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 4d40388..e429b93 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Principais aplicativos do Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Definir plano de fundo"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Não foi possível carregar a imagem"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Não foi possível carregar a imagem como plano de fundo"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d selecionados"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d selecionados"</item>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
index ddcf404..4aa90d4 100644
--- a/res/values-rm/strings.xml
+++ b/res/values-rm/strings.xml
@@ -28,6 +28,10 @@
     <string name="folder_name" msgid="7371454440695724752"></string>
     <!-- no translation found for wallpaper_instructions (563973358787555519) -->
     <skip />
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
     <!-- no translation found for number_of_items_selected:zero (7464587177007785408) -->
     <!-- no translation found for number_of_items_selected:one (142482526010824029) -->
     <!-- no translation found for number_of_items_selected:other (1418352074806573570) -->
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 78d9da4..c05e95e 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Setați imaginea de fundal"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Nu s-a putut încărca imaginea"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Nu s-a putut încărca imaginea ca fundal"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d selectate"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d selectat"</item>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 06fbd26..89da225 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Основные приложения Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Установить как обои"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Выбрано: %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Выбрано: %1$d"</item>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 29de526..01c7203 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Nastaviť tapetu"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Počet vybratých položiek: %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Počet vybratých položiek: %1$d"</item>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 021eca8..ac4f9e5 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Osnovne aplikacije sistema Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Nastavi ozadje"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Slike ni bilo mogoče naložiti"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Slike ni bilo mogoče naložiti kot ozadja"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Št. izbranih: %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Št. izbranih: %1$d"</item>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 06d80ef..8af530c 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Основне Android апликације"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Подеси позадину"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Изабранo je %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Изабрана je %1$d"</item>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index bf75200..93ebb2d 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Ange bakgrund"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Det gick inte att läsa in bilden"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Det gick inte att läsa in bilden som bakgrund"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d har valts"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d har valts"</item>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index c610f34..7cd66e7 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Programu Msingi za Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Weka mandhari"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Haikuweza kupakia picha"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Haikuweza kupakia picha kama mandhari"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d zimechaguliwa"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d zimechaguliwa"</item>
diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml
index eb8f83c..433a5d4 100644
--- a/res/values-sw720dp-land/dimens.xml
+++ b/res/values-sw720dp-land/dimens.xml
@@ -21,9 +21,6 @@
     <integer name="apps_customize_cling_focused_x">4</integer>
     <integer name="apps_customize_cling_focused_y">2</integer>
 
-<!-- Workspace -->
-    <dimen name="workspace_page_spacing">50dp</dimen>
-
     <!-- the area at the edge of the screen that makes the workspace go left
          or right while you're dragging. -->
     <dimen name="scroll_zone">100dip</dimen>
diff --git a/res/values-sw720dp-port/dimens.xml b/res/values-sw720dp-port/dimens.xml
index 62bdaaa..9fe312b 100644
--- a/res/values-sw720dp-port/dimens.xml
+++ b/res/values-sw720dp-port/dimens.xml
@@ -19,7 +19,6 @@
     <!-- the area at the edge of the screen that makes the workspace go left
          or right while you're dragging. -->
     <dimen name="scroll_zone">40dp</dimen>
-    <dimen name="workspace_page_spacing">24dp</dimen>
 
     <integer name="apps_customize_cling_focused_x">2</integer>
     <integer name="apps_customize_cling_focused_y">2</integer>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 68118a7..6e693ec 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"แอปหลักของแอนดรอยด์"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"ตั้งค่าวอลเปเปอร์"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"เลือกไว้ %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"เลือกไว้ %1$d"</item>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 56801b4..3a6096b 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Itakda ang wallpaper"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d ang napili"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d ang napili"</item>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index dbbdee2..89f70d7 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Android Çekirdek Uygulamaları"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Duvar kağıdını ayarla"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d tane seçildi"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d tane seçildi"</item>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index e0f3211..3be6935 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Базові програми Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Установити фон"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Не вдалося завантажити зображення"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Не вдалося завантажити зображення як фоновий малюнок"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Вибрано %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Вибрано %1$d"</item>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 3b081d0..12eeef2 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Ứng dụng lõi Android"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Đặt hình nền"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"Đã chọn %1$d"</item>
     <item quantity="one" msgid="142482526010824029">"Đã chọn %1$d"</item>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 43a57c4..4ab9b58 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -24,6 +24,10 @@
     <string name="uid_name" msgid="7820867637514617527">"Android 核心应用"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"设置壁纸"</string>
+    <!-- no translation found for image_load_fail (2821429163328561136) -->
+    <skip />
+    <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
+    <skip />
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"已选择%1$d项"</item>
     <item quantity="one" msgid="142482526010824029">"已选择%1$d项"</item>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index eb3def4..2a1f7db 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Android 核心應用程式"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"設定桌布"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"無法載入圖片"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"無法載入設為桌布的圖片"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"已選取 %1$d 個"</item>
     <item quantity="one" msgid="142482526010824029">"已選取 %1$d 個"</item>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index a68f163..6a0c18f 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Android 核心應用程式"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"設定桌布"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"無法載入圖片"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"無法載入設為桌布的圖片"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"已選取 %1$d 個項目"</item>
     <item quantity="one" msgid="142482526010824029">"已選取 %1$d 個項目"</item>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index da790e6..27e42ae 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -24,6 +24,8 @@
     <string name="uid_name" msgid="7820867637514617527">"Izinhlelo zokusebenza ze-Android Core"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="wallpaper_instructions" msgid="563973358787555519">"Setha isithombe sangemuva"</string>
+    <string name="image_load_fail" msgid="2821429163328561136">"Ayikwazanga ukulayisha isithombe"</string>
+    <string name="wallpaper_load_fail" msgid="1261270681127096352">"Ayikwazanga ukulayisha isithombe njengesithombe sangemuva"</string>
   <plurals name="number_of_items_selected">
     <item quantity="zero" msgid="7464587177007785408">"%1$d khethiwe"</item>
     <item quantity="one" msgid="142482526010824029">"%1$d khethiwe"</item>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index a2d3a83..0006a74 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -103,8 +103,7 @@
         <attr name="pageLayoutPaddingBottom" format="dimension" />
         <attr name="pageLayoutPaddingLeft" format="dimension" />
         <attr name="pageLayoutPaddingRight" format="dimension" />
-        <!-- The space between adjacent pages of the PagedView. -->
-        <attr name="pageSpacing" format="dimension" />
+
         <!-- The page indicator for this workspace -->
         <attr name="pageIndicator" format="reference" />
     </declare-styleable>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index dc35a3f..c073903 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -34,6 +34,7 @@
     <color name="apps_customize_icon_text_color">#FFF</color>
     <color name="wallpaper_picker_translucent_gray">#66000000</color>
     <color name="folder_items_text_color">#FF333333</color>
+    <color name="folder_items_glow_color">#FFCCCCCC</color>
     <color name="outline_color">#FFFFFFFF</color>
     
     <color name="first_run_cling_circle_background_color">#64b1ea</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b4b2367..d87fb14 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -20,6 +20,8 @@
     <dimen name="dynamic_grid_search_bar_max_width">500dp</dimen>
     <dimen name="dynamic_grid_search_bar_height">48dp</dimen>
     <dimen name="dynamic_grid_page_indicator_height">24dp</dimen>
+    <dimen name="dynamic_grid_icon_drawable_padding">4dp</dimen>
+    <dimen name="dynamic_grid_workspace_page_spacing">8dp</dimen>
 
 <!-- Wallpaper picker -->
     <dimen name="wallpaperThumbnailWidth">106.5dp</dimen>
@@ -40,7 +42,6 @@
 <!-- Workspace -->
     <dimen name="workspace_max_gap">16dp</dimen>
     <dimen name="workspace_overscroll_drawable_padding">0dp</dimen>
-    <dimen name="workspace_spring_loaded_page_spacing">15dp</dimen>
     <dimen name="overview_panel_bottom_padding">50dp</dimen>
     <dimen name="overview_panel_buttonSpacing">60dp</dimen>
     <dimen name="overview_mode_page_offset">130dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index cafa424..30f4d8f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -30,6 +30,14 @@
     <string name="folder_name"></string>
     <!-- Button label on Wallpaper picker screen; user selects this button to set a specific wallpaper -->
     <string name="wallpaper_instructions">Set wallpaper</string>
+    <!-- Error message when an image is selected as a wallpaper,
+         but the wallpaper picker cannot load it -->
+    <string name="image_load_fail">Coudn\'t load image</string>
+    <!-- Error message when an image is selected as a wallpaper,
+         but the wallpaper cropper cannot load it. The user will
+         usually see this when using another app and trying to set
+         an image as the wallpaper -->
+    <string name="wallpaper_load_fail">Couldn\'t load image as wallpaper</string>
     <!-- Shown when wallpapers are selected in Wallpaper picker -->
     <!-- String indicating how many media item(s) is(are) selected
             eg. 1 selected [CHAR LIMIT=30] -->
@@ -230,7 +238,7 @@
     <string name="apps_customize_widgets_scroll_format">Widgets page %1$d of %2$d</string>
 
     <!-- Clings -->
-    <!-- The title text for the workspace cling [CHAR_LIMIT=60] -->
+    <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
     <string name="first_run_cling_title">Welcome!</string>
     <!-- The description of how to use the workspace [CHAR_LIMIT=60] -->
     <string name="first_run_cling_description">Make yourself at home.</string>
@@ -240,17 +248,13 @@
     <string name="first_run_cling_search_bar_hint"></string>
     <!-- The description of how to use the workspace [CHAR_LIMIT=60] -->
     <string name="first_run_cling_create_screens_hint">Create more screens for apps and folders</string>
-    <!-- The title text for the workspace cling [CHAR_LIMIT=60] -->
+    <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
     <string name="workspace_cling_title">Organize your space</string>
-    <!-- The description of how to use the workspace [CHAR_LIMIT=160] -->
+    <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
     <string name="workspace_cling_move_item">Touch &amp; hold background to manage wallpaper, widgets and settings.</string>
-    <!-- The title text for the All Apps cling [CHAR_LIMIT=60] -->
-    <string name="all_apps_cling_title">Choose some apps</string>
-    <!-- The description of how to pick up and add an item to the workspace [CHAR_LIMIT=160] -->
-    <string name="all_apps_cling_add_item">To add an app to your Home screen, touch &amp; hold it.</string>
-    <!-- The title text for the Folder cling [CHAR_LIMIT=60] -->
+    <!-- The title text for the Folder cling [CHAR_LIMIT=30] -->
     <string name="folder_cling_title">Here\'s a folder</string>
-    <!-- The description of how to create a folder [CHAR_LIMIT=160] -->
+    <!-- The description of how to create a folder [CHAR_LIMIT=70] -->
     <string name="folder_cling_create_folder">To create one like this, touch &amp; hold an app, then move it over another.</string>
     <!-- The text on the button to dismiss a cling [CHAR_LIMIT=30] -->
     <string name="cling_dismiss">OK</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index a1d2c5c..39e55d8 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -98,7 +98,7 @@
     <style name="WorkspaceIcon.AppsCustomize">
         <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:drawablePadding">@dimen/dynamic_grid_icon_drawable_padding</item>
         <item name="android:shadowRadius">4.0</item>
         <item name="android:shadowColor">#FF000000</item>
     </style>
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index 9b35bb5..25cd9f2 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -811,13 +811,23 @@
                 !(target instanceof DeleteDropTarget))) {
             // Exit spring loaded mode if we have not successfully dropped or have not handled the
             // drop in Workspace
-            mLauncher.exitSpringLoadedDragMode();
+            mLauncher.getWorkspace().removeExtraEmptyScreen(true, new Runnable() {
+                @Override
+                public void run() {
+                    mLauncher.exitSpringLoadedDragMode();
+                    mLauncher.unlockScreenOrientation(false);
+                }
+            });
+        } else {
+            mLauncher.unlockScreenOrientation(false);
         }
-        mLauncher.unlockScreenOrientation(false);
     }
 
     @Override
     public View getContent() {
+        if (getChildCount() > 0) {
+            return getChildAt(0);
+        }
         return null;
     }
 
@@ -841,7 +851,7 @@
     public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
         mInTransition = false;
         for (AsyncTaskPageData d : mDeferredSyncWidgetPageItems) {
-            onSyncWidgetPageItems(d);
+            onSyncWidgetPageItems(d, false);
         }
         mDeferredSyncWidgetPageItems.clear();
         for (Runnable r : mDeferredPrepareLoadWidgetPreviewsTasks) {
@@ -935,12 +945,12 @@
     }
 
     public void setContentType(ContentType type) {
-        int page = getCurrentPage();
-        if (mContentType != type) {
-            page = 0;
+        // Widgets appear to be cleared every time you leave, always force invalidate for them
+        if (mContentType != type || type == ContentType.Widgets) {
+            int page = (mContentType != type) ? 0 : getCurrentPage();
+            mContentType = type;
+            invalidatePageData(page, true);
         }
-        mContentType = type;
-        invalidatePageData(page, true);
     }
 
     public ContentType getContentType() {
@@ -1110,7 +1120,7 @@
                     mRunningTasks.remove(task);
                     if (task.isCancelled()) return;
                     // do cleanup inside onSyncWidgetPageItems
-                    onSyncWidgetPageItems(data);
+                    onSyncWidgetPageItems(data, false);
                 }
             }, mWidgetPreviewLoader);
 
@@ -1226,7 +1236,7 @@
                     AsyncTaskPageData data = new AsyncTaskPageData(page, items,
                             maxPreviewWidth, maxPreviewHeight, null, null, mWidgetPreviewLoader);
                     loadWidgetPreviewsInBackground(null, data);
-                    onSyncWidgetPageItems(data);
+                    onSyncWidgetPageItems(data, immediate);
                 } else {
                     if (mInTransition) {
                         mDeferredPrepareLoadWidgetPreviewsTasks.add(this);
@@ -1265,8 +1275,8 @@
         }
     }
 
-    private void onSyncWidgetPageItems(AsyncTaskPageData data) {
-        if (mInTransition) {
+    private void onSyncWidgetPageItems(AsyncTaskPageData data, boolean immediatelySyncItems) {
+        if (!immediatelySyncItems && mInTransition) {
             mDeferredSyncWidgetPageItems.add(data);
             return;
         }
diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java
index bfcf92a..697bd7e 100644
--- a/src/com/android/launcher3/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher3/AppsCustomizeTabHost.java
@@ -367,6 +367,10 @@
 
     @Override
     public View getContent() {
+        View appsCustomizeContent = mAppsCustomizePane.getContent();
+        if (appsCustomizeContent != null) {
+            return appsCustomizeContent;
+        }
         return mContent;
     }
 
@@ -397,6 +401,7 @@
 
     @Override
     public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
+        mAppsCustomizePane.onLauncherTransitionStart(l, animated, toWorkspace);
         if (animated) {
             enableAndBuildHardwareLayer();
         }
@@ -407,7 +412,7 @@
 
     @Override
     public void onLauncherTransitionStep(Launcher l, float t) {
-        // Do nothing
+        mAppsCustomizePane.onLauncherTransitionStep(l, t);
     }
 
     @Override
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 8dab943..30016e5 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -87,7 +87,7 @@
         // Ensure we are using the right text size
         LauncherAppState app = LauncherAppState.getInstance();
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.iconTextSize);
+        setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
         setTextColor(getResources().getColor(R.color.workspace_icon_text_color));
     }
 
@@ -111,7 +111,7 @@
 
         setCompoundDrawables(null,
                 Utilities.createIconDrawable(b), null, null);
-        setCompoundDrawablePadding((int) ((grid.folderIconSizePx - grid.iconSizePx) / 2f));
+        setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
         setText(info.title);
         setTag(info);
     }
@@ -203,6 +203,10 @@
         destCanvas.restore();
     }
 
+    public void setGlowColor(int color) {
+        mFocusedOutlineColor = mFocusedGlowColor = mPressedOutlineColor = mPressedGlowColor = color;
+    }
+
     /**
      * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
      * Responsibility for the bitmap is transferred to the caller.
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index dafb79f..df16d41 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -76,6 +76,7 @@
     private int mHeightGap;
     private int mMaxGap;
     private boolean mScrollingTransformsDirty = false;
+    private boolean mDropPending = false;
 
     private final Rect mRect = new Rect();
     private final CellInfo mCellInfo = new CellInfo();
@@ -214,7 +215,7 @@
         setAlwaysDrawnWithCacheEnabled(false);
 
         final Resources res = getResources();
-        mHotseatScale = (float) grid.hotseatIconSize / grid.iconSize;
+        mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx;
 
         mNormalBackground = res.getDrawable(R.drawable.screenpanel);
         mActiveGlowBackground = res.getDrawable(R.drawable.screenpanel_hover);
@@ -332,6 +333,14 @@
         mShortcutsAndWidgets.setInvertIfRtl(invert);
     }
 
+    public void setDropPending(boolean pending) {
+        mDropPending = pending;
+    }
+
+    public boolean isDropPending() {
+        return mDropPending;
+    }
+
     private void invalidateBubbleTextView(BubbleTextView icon) {
         final int padding = icon.getPressedOrFocusedBackgroundPadding();
         invalidate(icon.getLeft() + getPaddingLeft() - padding,
diff --git a/src/com/android/launcher3/Cling.java b/src/com/android/launcher3/Cling.java
index 338b722..9974acd 100644
--- a/src/com/android/launcher3/Cling.java
+++ b/src/com/android/launcher3/Cling.java
@@ -147,7 +147,7 @@
                     pos.left + Utilities.sIconTextureWidth,
                     pos.top + Utilities.sIconTextureHeight);
             Utilities.scaleRectAboutCenter(mFocusedHotseatAppBounds,
-                    (grid.hotseatIconSize / grid.iconSize));
+                    ((float) grid.hotseatIconSizePx / grid.iconSizePx));
 
             // Set the title
             TextView v = (TextView) findViewById(R.id.focused_hotseat_app_title);
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java
index 5b5c35c..5e733f0 100644
--- a/src/com/android/launcher3/DragController.java
+++ b/src/com/android/launcher3/DragController.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -25,14 +26,8 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.ViewConfiguration;
+import android.view.*;
 import android.view.inputmethod.InputMethodManager;
-
 import com.android.launcher3.R;
 
 import java.util.ArrayList;
@@ -323,7 +318,7 @@
         }
         endDrag();
     }
-    public void onAppsRemoved(ArrayList<AppInfo> appInfos, Context context) {
+    public void onAppsRemoved(final ArrayList<String> packageNames, ArrayList<AppInfo> appInfos) {
         // Cancel the current drag if we are removing an app that we are dragging
         if (mDragObject != null) {
             Object rawDragInfo = mDragObject.dragInfo;
@@ -333,8 +328,9 @@
                     // Added null checks to prevent NPE we've seen in the wild
                     if (dragInfo != null &&
                         dragInfo.intent != null) {
-                        boolean isSameComponent =
-                                dragInfo.intent.getComponent().equals(info.componentName);
+                        ComponentName cn = dragInfo.intent.getComponent();
+                        boolean isSameComponent = cn.equals(info.componentName) ||
+                                packageNames.contains(cn.getPackageName());
                         if (isSameComponent) {
                             cancelDrag();
                             return;
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 89f8275..159d7d9 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -480,7 +480,7 @@
     }
 
     public void animateViewIntoPosition(DragView dragView, final View child) {
-        animateViewIntoPosition(dragView, child, null);
+        animateViewIntoPosition(dragView, child, null, null);
     }
 
     public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha,
@@ -496,8 +496,8 @@
     }
 
     public void animateViewIntoPosition(DragView dragView, final View child,
-            final Runnable onFinishAnimationRunnable) {
-        animateViewIntoPosition(dragView, child, -1, onFinishAnimationRunnable, null);
+            final Runnable onFinishAnimationRunnable, View anchorView) {
+        animateViewIntoPosition(dragView, child, -1, onFinishAnimationRunnable, anchorView);
     }
 
     public void animateViewIntoPosition(DragView dragView, final View child, int duration,
@@ -645,8 +645,10 @@
                 int x = (int) (fromLeft + Math.round(((to.left - fromLeft) * motionPercent)));
                 int y = (int) (fromTop + Math.round(((to.top - fromTop) * motionPercent)));
 
-                int xPos = x - mDropView.getScrollX() + (mAnchorView != null
-                        ? (mAnchorViewInitialScrollX - mAnchorView.getScrollX()) : 0);
+                int anchorAdjust = mAnchorView == null ? 0 : (int) (mAnchorView.getScaleX() *
+                    (mAnchorViewInitialScrollX - mAnchorView.getScrollX()));
+
+                int xPos = x - mDropView.getScrollX() + anchorAdjust;
                 int yPos = y - mDropView.getScrollY();
 
                 mDropView.setTranslationX(xPos);
diff --git a/src/com/android/launcher3/DrawableTileSource.java b/src/com/android/launcher3/DrawableTileSource.java
new file mode 100644
index 0000000..c1f2eff
--- /dev/null
+++ b/src/com/android/launcher3/DrawableTileSource.java
@@ -0,0 +1,102 @@
+/*
+ * 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.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+import com.android.gallery3d.glrenderer.BasicTexture;
+import com.android.gallery3d.glrenderer.BitmapTexture;
+import com.android.photos.views.TiledImageRenderer;
+
+public class DrawableTileSource implements TiledImageRenderer.TileSource {
+    private static final int GL_SIZE_LIMIT = 2048;
+    // This must be no larger than half the size of the GL_SIZE_LIMIT
+    // due to decodePreview being allowed to be up to 2x the size of the target
+    public static final int MAX_PREVIEW_SIZE = GL_SIZE_LIMIT / 2;
+
+    private int mTileSize;
+    private int mPreviewSize;
+    private Drawable mDrawable;
+    private BitmapTexture mPreview;
+
+    public DrawableTileSource(Context context, Drawable d, int previewSize) {
+        mTileSize = TiledImageRenderer.suggestedTileSize(context);
+        mDrawable = d;
+        mPreviewSize = Math.min(previewSize, MAX_PREVIEW_SIZE);
+    }
+
+    @Override
+    public int getTileSize() {
+        return mTileSize;
+    }
+
+    @Override
+    public int getImageWidth() {
+        return mDrawable.getIntrinsicWidth();
+    }
+
+    @Override
+    public int getImageHeight() {
+        return mDrawable.getIntrinsicHeight();
+    }
+
+    @Override
+    public int getRotation() {
+        return 0;
+    }
+
+    @Override
+    public BasicTexture getPreview() {
+        if (mPreviewSize == 0) {
+            return null;
+        }
+        if (mPreview == null){
+            float width = getImageWidth();
+            float height = getImageHeight();
+            while (width > MAX_PREVIEW_SIZE || height > MAX_PREVIEW_SIZE) {
+                width /= 2;
+                height /= 2;
+            }
+            Bitmap b = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
+            Canvas c = new Canvas(b);
+            mDrawable.setBounds(new Rect(0, 0, (int) width, (int) height));
+            mDrawable.draw(c);
+            c.setBitmap(null);
+            mPreview = new BitmapTexture(b);
+        }
+        return mPreview;
+    }
+
+    @Override
+    public Bitmap getTile(int level, int x, int y, Bitmap bitmap) {
+        int tileSize = getTileSize();
+        if (bitmap == null) {
+            bitmap = Bitmap.createBitmap(tileSize, tileSize, Bitmap.Config.ARGB_8888);
+        }
+        Canvas c = new Canvas(bitmap);
+        Rect bounds = new Rect(0, 0, getImageWidth(), getImageHeight());
+        bounds.offset(-x, -y);
+        mDrawable.setBounds(bounds);
+        mDrawable.draw(c);
+        c.setBitmap(null);
+        return bitmap;
+    }
+}
diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java
index 4776c86..ccbac2c 100644
--- a/src/com/android/launcher3/DynamicGrid.java
+++ b/src/com/android/launcher3/DynamicGrid.java
@@ -23,13 +23,17 @@
 import android.content.res.Resources;
 import android.graphics.Paint;
 import android.graphics.Paint.FontMetrics;
+import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
+import android.view.Display;
 import android.view.Gravity;
+import android.view.Surface;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
 import android.widget.FrameLayout;
 
 import java.util.ArrayList;
@@ -52,15 +56,20 @@
 }
 
 class DeviceProfile {
+    public static interface DeviceProfileCallbacks {
+        public void onAvailableSizeChanged(DeviceProfile grid);
+    }
+
     String name;
     float minWidthDps;
     float minHeightDps;
     float numRows;
     float numColumns;
-    float iconSize;
-    float iconTextSize;
     float numHotseatIcons;
-    float hotseatIconSize;
+    private float iconSize;
+    private float iconTextSize;
+    private int iconDrawablePaddingOriginalPx;
+    private float hotseatIconSize;
 
     boolean isLandscape;
     boolean isTablet;
@@ -75,8 +84,11 @@
     int heightPx;
     int availableWidthPx;
     int availableHeightPx;
+    int defaultPageSpacingPx;
+
     int iconSizePx;
     int iconTextSizePx;
+    int iconDrawablePaddingPx;
     int cellWidthPx;
     int cellHeightPx;
     int folderBackgroundOffset;
@@ -96,6 +108,8 @@
     int searchBarHeightPx;
     int pageIndicatorHeightPx;
 
+    private ArrayList<DeviceProfileCallbacks> mCallbacks = new ArrayList<DeviceProfileCallbacks>();
+
     DeviceProfile(String n, float w, float h, float r, float c,
                   float is, float its, float hs, float his) {
         // Ensure that we have an odd number of hotseat items (since we need to place all apps)
@@ -119,21 +133,24 @@
                   float minWidth, float minHeight,
                   int wPx, int hPx,
                   int awPx, int ahPx,
-                  Resources resources) {
-        DisplayMetrics dm = resources.getDisplayMetrics();
+                  Resources res) {
+        DisplayMetrics dm = res.getDisplayMetrics();
         ArrayList<DeviceProfileQuery> points =
                 new ArrayList<DeviceProfileQuery>();
         transposeLayoutWithOrientation =
-                resources.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
+                res.getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
         minWidthDps = minWidth;
         minHeightDps = minHeight;
 
         ComponentName cn = new ComponentName(context.getPackageName(),
                 this.getClass().getName());
         defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
-        edgeMarginPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
+        edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
         desiredWorkspaceLeftRightMarginPx = 2 * edgeMarginPx;
-        pageIndicatorHeightPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
+        pageIndicatorHeightPx =
+                res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
+        defaultPageSpacingPx =
+                res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
 
         // Interpolate the rows
         for (DeviceProfile p : profiles) {
@@ -146,13 +163,20 @@
             points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numColumns));
         }
         numColumns = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
+        // Interpolate the hotseat length
+        points.clear();
+        for (DeviceProfile p : profiles) {
+            points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numHotseatIcons));
+        }
+        numHotseatIcons = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
+        hotseatAllAppsRank = (int) (numHotseatIcons / 2);
+
         // Interpolate the icon size
         points.clear();
         for (DeviceProfile p : profiles) {
             points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconSize));
         }
         iconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
-        iconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
 
         // Interpolate the icon text size
         points.clear();
@@ -160,14 +184,9 @@
             points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconTextSize));
         }
         iconTextSize = invDistWeightedInterpolate(minWidth, minHeight, points);
-        iconTextSizePx = DynamicGrid.pxFromSp(iconTextSize, dm);
+        iconDrawablePaddingOriginalPx =
+                res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
 
-        // Interpolate the hotseat size
-        points.clear();
-        for (DeviceProfile p : profiles) {
-            points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numHotseatIcons));
-        }
-        numHotseatIcons = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
         // Interpolate the hotseat icon size
         points.clear();
         for (DeviceProfile p : profiles) {
@@ -175,11 +194,91 @@
         }
         // Hotseat
         hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
-        hotseatIconSizePx = DynamicGrid.pxFromDp(hotseatIconSize, dm);
-        hotseatAllAppsRank = (int) (numColumns / 2);
 
-        // Calculate other vars based on Configuration
-        updateFromConfiguration(resources, wPx, hPx, awPx, ahPx);
+        // Calculate the remaining vars
+        updateFromConfiguration(context, res, wPx, hPx, awPx, ahPx);
+        updateAvailableDimensions(context);
+    }
+
+    void addCallback(DeviceProfileCallbacks cb) {
+        mCallbacks.add(cb);
+        cb.onAvailableSizeChanged(this);
+    }
+    void removeCallback(DeviceProfileCallbacks cb) {
+        mCallbacks.remove(cb);
+    }
+
+    private int getDeviceOrientation(Context context) {
+        WindowManager windowManager =  (WindowManager)
+                context.getSystemService(Context.WINDOW_SERVICE);
+        Resources resources = context.getResources();
+        DisplayMetrics dm = resources.getDisplayMetrics();
+        Configuration config = resources.getConfiguration();
+        int rotation = windowManager.getDefaultDisplay().getRotation();
+
+        boolean isLandscape = (config.orientation == Configuration.ORIENTATION_LANDSCAPE) &&
+                (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180);
+        boolean isRotatedPortrait = (config.orientation == Configuration.ORIENTATION_PORTRAIT) &&
+                (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+        if (isLandscape || isRotatedPortrait) {
+            return CellLayout.LANDSCAPE;
+        } else {
+            return CellLayout.PORTRAIT;
+        }
+    }
+
+    private void updateAvailableDimensions(Context context) {
+        WindowManager windowManager =  (WindowManager)
+                context.getSystemService(Context.WINDOW_SERVICE);
+        Display display = windowManager.getDefaultDisplay();
+        Resources resources = context.getResources();
+        DisplayMetrics dm = resources.getDisplayMetrics();
+        Configuration config = resources.getConfiguration();
+
+        // There are three possible configurations that the dynamic grid accounts for, portrait,
+        // landscape with the nav bar at the bottom, and landscape with the nav bar at the side.
+        // To prevent waiting for fitSystemWindows(), we make the observation that in landscape,
+        // the height is the smallest height (either with the nav bar at the bottom or to the
+        // side) and otherwise, the height is simply the largest possible height for a portrait
+        // device.
+        Point size = new Point();
+        Point smallestSize = new Point();
+        Point largestSize = new Point();
+        display.getSize(size);
+        display.getCurrentSizeRange(smallestSize, largestSize);
+        availableWidthPx = size.x;
+        if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            availableHeightPx = smallestSize.y;
+        } else {
+            availableHeightPx = largestSize.y;
+        }
+
+        // Check to see if the icons fit in the new available height.  If not, then we need to
+        // shrink the icon size.
+        Rect workspacePadding = getWorkspacePadding();
+        float scale = 1f;
+        int drawablePadding = iconDrawablePaddingOriginalPx;
+        updateIconSize(1f, drawablePadding, resources, dm);
+        float usedHeight = (cellHeightPx * numRows);
+        int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom);
+        if (usedHeight > maxHeight) {
+            scale = maxHeight / usedHeight;
+            drawablePadding = 0;
+        }
+        updateIconSize(scale, drawablePadding, resources, dm);
+
+        // Make the callbacks
+        for (DeviceProfileCallbacks cb : mCallbacks) {
+            cb.onAvailableSizeChanged(this);
+        }
+    }
+
+    private void updateIconSize(float scale, int drawablePadding, Resources resources,
+                                DisplayMetrics dm) {
+        iconSizePx = (int) (DynamicGrid.pxFromDp(iconSize, dm) * scale);
+        iconTextSizePx = (int) (DynamicGrid.pxFromSp(iconTextSize, dm) * scale);
+        iconDrawablePaddingPx = drawablePadding;
+        hotseatIconSizePx = (int) (DynamicGrid.pxFromDp(hotseatIconSize, dm) * scale);
 
         // Search Bar
         searchBarSpaceMaxWidthPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_max_width);
@@ -192,23 +291,7 @@
         textPaint.setTextSize(iconTextSizePx);
         FontMetrics fm = textPaint.getFontMetrics();
         cellWidthPx = iconSizePx;
-        cellHeightPx = iconSizePx + (int) Math.ceil(fm.bottom - fm.top);
-
-        // At this point, if the cells do not fit into the available height, then we need
-        // to shrink the icon size
-        /*
-        Rect padding = getWorkspacePadding(isLandscape ?
-                CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
-        int h = (int) (numRows * cellHeightPx) + padding.top + padding.bottom;
-        if (h > availableHeightPx) {
-            float delta = h - availableHeightPx;
-            int deltaPx = (int) Math.ceil(delta / numRows);
-            iconSizePx -= deltaPx;
-            iconSize = DynamicGrid.dpiFromPx(iconSizePx, dm);
-            cellWidthPx = iconSizePx;
-            cellHeightPx = iconSizePx + (int) Math.ceil(fm.bottom - fm.top);
-        }
-        */
+        cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top);
 
         // Hotseat
         hotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx;
@@ -217,12 +300,26 @@
 
         // Folder
         folderCellWidthPx = cellWidthPx + 3 * edgeMarginPx;
-        folderCellHeightPx = cellHeightPx + (int) ((3f/2f) * edgeMarginPx);
+        folderCellHeightPx = cellHeightPx + edgeMarginPx;
         folderBackgroundOffset = -edgeMarginPx;
         folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
+
+        // All Apps
+        Rect padding = getWorkspacePadding(isLandscape ?
+                CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
+        int pageIndicatorOffset =
+                resources.getDimensionPixelSize(R.dimen.apps_customize_page_indicator_offset);
+        if (isLandscape) {
+            allAppsNumRows = (availableHeightPx - pageIndicatorOffset - 4 * edgeMarginPx) /
+                    (iconSizePx + iconTextSizePx + 2 * edgeMarginPx);
+        } else {
+            allAppsNumRows = (int) numRows + 1;
+        }
+        allAppsNumCols = (availableWidthPx - padding.left - padding.right - 2 * edgeMarginPx) /
+                (iconSizePx + 2 * edgeMarginPx);
     }
 
-    void updateFromConfiguration(Resources resources, int wPx, int hPx,
+    void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx,
                                  int awPx, int ahPx) {
         isLandscape = (resources.getConfiguration().orientation ==
                 Configuration.ORIENTATION_LANDSCAPE);
@@ -233,18 +330,7 @@
         availableWidthPx = awPx;
         availableHeightPx = ahPx;
 
-        Rect padding = getWorkspacePadding(isLandscape ?
-                CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
-        int pageIndicatorOffset =
-            resources.getDimensionPixelSize(R.dimen.apps_customize_page_indicator_offset);
-        if (isLandscape) {
-            allAppsNumRows = (availableHeightPx - pageIndicatorOffset - 4 * edgeMarginPx) /
-                    (iconSizePx + iconTextSizePx + 2 * edgeMarginPx);
-        } else {
-            allAppsNumRows = (int) numRows + 1;
-        }
-        allAppsNumCols = (availableWidthPx - padding.left - padding.right - 2 * edgeMarginPx) /
-                (iconSizePx + 2 * edgeMarginPx);
+        updateAvailableDimensions(context);
     }
 
     private float dist(PointF p0, PointF p1) {
@@ -298,6 +384,10 @@
         return sum;
     }
 
+    Rect getWorkspacePadding() {
+        return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
+    }
+
     Rect getWorkspacePadding(int orientation) {
         Rect padding = new Rect();
         if (orientation == CellLayout.LANDSCAPE &&
@@ -331,6 +421,19 @@
         return padding;
     }
 
+    int getWorkspacePageSpacing(int orientation) {
+        if (orientation == CellLayout.LANDSCAPE &&
+                transposeLayoutWithOrientation) {
+            // In landscape mode the page spacing is set to the default.
+            return defaultPageSpacingPx;
+        } else {
+            // In portrait, we want the pages spaced such that there is no
+            // overhang of the previous / next page into the current page viewport.
+            // We assume symmetrical padding in portrait mode.
+            return getWorkspacePadding().left;
+        }
+    }
+
     // The rect returned will be extended to below the system ui that covers the workspace
     Rect getHotseatRect() {
         if (isVerticalBarLayout()) {
@@ -363,6 +466,10 @@
         return isLandscape && transposeLayoutWithOrientation;
     }
 
+    boolean shouldFadeAdjacentWorkspaceScreens() {
+        return isVerticalBarLayout() || isLargeTablet();
+    }
+
     public void layout(Launcher launcher) {
         FrameLayout.LayoutParams lp;
         Resources res = launcher.getResources();
@@ -413,15 +520,14 @@
         }
 
         // Layout the workspace
-        View workspace = launcher.findViewById(R.id.workspace);
+        PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace);
         lp = (FrameLayout.LayoutParams) workspace.getLayoutParams();
         lp.gravity = Gravity.CENTER;
-        Rect padding = getWorkspacePadding(isLandscape
-                ? CellLayout.LANDSCAPE
-                : CellLayout.PORTRAIT);
-        workspace.setPadding(padding.left, padding.top,
-                padding.right, padding.bottom);
+        int orientation = isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT;
+        Rect padding = getWorkspacePadding(orientation);
         workspace.setLayoutParams(lp);
+        workspace.setPadding(padding.left, padding.top, padding.right, padding.bottom);
+        workspace.setPageSpacing(getWorkspacePageSpacing(orientation));
 
         // Layout the hotseat
         View hotseat = launcher.findViewById(R.id.hotseat);
@@ -431,8 +537,7 @@
             lp.gravity = Gravity.RIGHT;
             lp.width = hotseatBarHeightPx;
             lp.height = LayoutParams.MATCH_PARENT;
-            hotseat.setPadding(0, 2 * edgeMarginPx,
-                    2 * edgeMarginPx, 2 * edgeMarginPx);
+            hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx);
         } else if (isTablet()) {
             // Pad the hotseat with the grid gap calculated above
             int gridGap = (int) ((widthPx - 2 * edgeMarginPx -
@@ -553,7 +658,7 @@
                 "Wd: " + mProfile.minWidthDps + ", Hd: " + mProfile.minHeightDps +
                 ", W: " + mProfile.widthPx + ", H: " + mProfile.heightPx +
                 " [r: " + mProfile.numRows + ", c: " + mProfile.numColumns +
-                ", is: " + mProfile.iconSizePx + ", its: " + mProfile.iconTextSize +
+                ", is: " + mProfile.iconSizePx + ", its: " + mProfile.iconTextSizePx +
                 ", cw: " + mProfile.cellWidthPx + ", ch: " + mProfile.cellHeightPx +
                 ", hc: " + mProfile.numHotseatIcons + ", his: " + mProfile.hotseatIconSizePx + "]";
     }
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 14760c7..bce6707 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -67,6 +67,7 @@
 
     public void setFilterBitmap(boolean filterBitmap) {
         mPaint.setFilterBitmap(filterBitmap);
+        mPaint.setAntiAlias(filterBitmap);
     }
 
     public int getAlpha() {
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 69d9a3d..bd61010 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -538,6 +538,7 @@
         textView.setTag(item);
         textView.setTextColor(getResources().getColor(R.color.folder_items_text_color));
         textView.setShadowsEnabled(false);
+        textView.setGlowColor(getResources().getColor(R.color.folder_items_glow_color));
 
         textView.setOnClickListener(this);
         textView.setOnLongClickListener(this);
@@ -956,9 +957,7 @@
         Rect workspacePadding = grid.getWorkspacePadding(grid.isLandscape ?
                 CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
         int maxContentAreaHeight = grid.availableHeightPx -
-                4 * grid.edgeMarginPx -
                 workspacePadding.top - workspacePadding.bottom -
-                getPaddingTop() - getPaddingBottom() -
                 mFolderNameHeight;
         return Math.min(maxContentAreaHeight,
                 mContent.getDesiredHeight());
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index cd1ff2c..5e461a7 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -37,7 +37,7 @@
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
+import android.widget.FrameLayout;
 import android.widget.TextView;
 
 import com.android.launcher3.R;
@@ -49,7 +49,7 @@
 /**
  * An icon that can appear on in the workspace representing an {@link UserFolder}.
  */
-public class FolderIcon extends LinearLayout implements FolderListener {
+public class FolderIcon extends FrameLayout implements FolderListener {
     private Launcher mLauncher;
     private Folder mFolder;
     private FolderInfo mInfo;
@@ -134,17 +134,20 @@
                     "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " +
                     "is dependent on this");
         }
+        LauncherAppState app = LauncherAppState.getInstance();
+        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
 
         FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
         icon.setClipToPadding(false);
         icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
         icon.mFolderName.setText(folderInfo.title);
-        icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+        icon.mFolderName.setCompoundDrawablePadding(0);
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams();
+        lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
+
         // Offset the preview background to center this view accordingly
-        LinearLayout.LayoutParams lp =
-                (LinearLayout.LayoutParams) icon.mPreviewBackground.getLayoutParams();
+        icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);
+        lp = (FrameLayout.LayoutParams) icon.mPreviewBackground.getLayoutParams();
         lp.topMargin = grid.folderBackgroundOffset;
         lp.width = grid.folderIconSizePx;
         lp.height = grid.folderIconSizePx;
@@ -537,12 +540,10 @@
         if (d != null) {
             mOldBounds.set(d.getBounds());
             d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
-            d.setFilterBitmap(true);
             d.setColorFilter(Color.argb(params.overlayAlpha, 255, 255, 255),
                     PorterDuff.Mode.SRC_ATOP);
             d.draw(canvas);
             d.clearColorFilter();
-            d.setFilterBitmap(false);
             d.setBounds(mOldBounds);
         }
         canvas.restore();
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 543b8ee..a55fce0 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -156,7 +156,7 @@
             Iterator<Entry<ComponentName, CacheEntry>> it = mCache.entrySet().iterator();
             while (it.hasNext()) {
                 final CacheEntry e = it.next().getValue();
-                if (e.icon.getWidth() != grid.iconSizePx || e.icon.getHeight() != grid.iconSizePx) {
+                if (e.icon.getWidth() < grid.iconSizePx || e.icon.getHeight() < grid.iconSizePx) {
                     it.remove();
                 }
             }
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 7df73b1..835c472 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -108,6 +108,9 @@
 
     public static void removeFromInstallQueue(SharedPreferences sharedPrefs,
                                               ArrayList<String> packageNames) {
+        if (packageNames.isEmpty()) {
+            return;
+        }
         synchronized(sLock) {
             Set<String> strings = sharedPrefs.getStringSet(APPS_PENDING_INSTALL, null);
             if (DBG) {
@@ -220,16 +223,8 @@
         }
         // This name is only used for comparisons and notifications, so fall back to activity name
         // if not supplied
-        String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
-        if (name == null) {
-            try {
-                PackageManager pm = context.getPackageManager();
-                ActivityInfo info = pm.getActivityInfo(intent.getComponent(), 0);
-                name = info.loadLabel(pm).toString();
-            } catch (PackageManager.NameNotFoundException nnfe) {
-                return;
-            }
-        }
+        String name = ensureValidName(context, intent,
+                data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME)).toString();
         Bitmap icon = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON);
         Intent.ShortcutIconResource iconResource =
             data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE);
@@ -315,6 +310,25 @@
                     Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
         }
         LauncherAppState app = LauncherAppState.getInstance();
-        return app.getModel().infoFromShortcutIntent(context, data, null);
+        ShortcutInfo info = app.getModel().infoFromShortcutIntent(context, data, null);
+        info.title = ensureValidName(context, launchIntent, info.title);
+        return info;
+    }
+
+    /**
+     * Ensures that we have a valid, non-null name.  If the provided name is null, we will return
+     * the application name instead.
+     */
+    private static CharSequence ensureValidName(Context context, Intent intent, CharSequence name) {
+        if (name == null) {
+            try {
+                PackageManager pm = context.getPackageManager();
+                ActivityInfo info = pm.getActivityInfo(intent.getComponent(), 0);
+                name = info.loadLabel(pm).toString();
+            } catch (PackageManager.NameNotFoundException nnfe) {
+                return "";
+            }
+        }
+        return name;
     }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b0e4968..3290d8e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -197,8 +197,8 @@
     private AnimatorSet mStateAnimation;
 
     static final int APPWIDGET_HOST_ID = 1024;
-    private static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
-    private static final int EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT = 600;
+    public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
+    private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
     private static final int SHOW_CLING_DURATION = 250;
     private static final int DISMISS_CLING_DURATION = 200;
 
@@ -305,6 +305,7 @@
     private Drawable mWorkspaceBackgroundDrawable;
 
     private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
+    private static final boolean DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE = false;
 
     static final ArrayList<String> sDumpLogs = new ArrayList<String>();
     static Date sDateStamp = new Date();
@@ -390,6 +391,7 @@
         display.getRealSize(realSize);
         DisplayMetrics dm = new DisplayMetrics();
         display.getMetrics(dm);
+
         // Lazy-initialize the dynamic grid
         DeviceProfile grid = app.initDynamicGrid(this,
                 Math.min(smallestSize.x, smallestSize.y),
@@ -437,18 +439,12 @@
         mSavedState = savedInstanceState;
         restoreState(mSavedState);
 
-        // Update customization drawer _after_ restoring the states
-        if (mAppsCustomizeContent != null) {
-            mAppsCustomizeContent.onPackagesUpdated(
-                LauncherModel.getSortedWidgetsAndShortcuts(this));
-        }
-
         if (PROFILE_STARTUP) {
             android.os.Debug.stopMethodTracing();
         }
 
         if (!mRestoring) {
-            if (sPausedFromUserAction) {
+            if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE || sPausedFromUserAction) {
                 // If the user leaves launcher, then we should just load items asynchronously when
                 // they return.
                 mModel.startLoader(true, -1);
@@ -709,13 +705,24 @@
         // Reset the startActivity waiting flag
         mWaitingForResult = false;
 
+        Runnable exitSpringLoaded = new Runnable() {
+            @Override
+            public void run() {
+                exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
+                        EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+            }
+        };
+
         if (requestCode == REQUEST_BIND_APPWIDGET) {
-            int appWidgetId = data != null ?
+            final int appWidgetId = data != null ?
                     data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
             if (resultCode == RESULT_CANCELED) {
                 completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
+                mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded,
+                        ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
             } else if (resultCode == RESULT_OK) {
-                addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, mPendingAddWidgetInfo);
+                addAppWidgetImpl(appWidgetId, mPendingAddInfo, null,
+                        mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
             }
             return;
         } else if (requestCode == REQUEST_PICK_WALLPAPER) {
@@ -725,22 +732,41 @@
             return;
         }
 
-        boolean delayExitSpringLoadedMode = false;
         boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
                 requestCode == REQUEST_CREATE_APPWIDGET);
 
         // We have special handling for widgets
         if (isWidgetDrop) {
-            int appWidgetId = data != null ?
+            final int appWidgetId = data != null ?
                     data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
+            final int result;
+            final Runnable onComplete;
             if (appWidgetId < 0) {
                 Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not returned from the \\" +
                         "widget configuration activity.");
-                completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
-                mWorkspace.stripEmptyScreens();
+                result = RESULT_CANCELED;
+                completeTwoStageWidgetDrop(result, appWidgetId);
+                onComplete = new Runnable() {
+                    @Override
+                    public void run() {
+                        exitSpringLoadedDragModeDelayed(false, 0, null);
+                    }
+                };
             } else {
-                completeTwoStageWidgetDrop(resultCode, appWidgetId);
+                result = resultCode;
+                final CellLayout dropLayout =
+                        (CellLayout) mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
+                dropLayout.setDropPending(true);
+                onComplete = new Runnable() {
+                    @Override
+                    public void run() {
+                        completeTwoStageWidgetDrop(result, appWidgetId);
+                        dropLayout.setDropPending(false);
+                    }
+                };
             }
+            mWorkspace.removeExtraEmptyScreen(true, onComplete, ON_ACTIVITY_RESULT_ANIMATION_DELAY,
+                    false);
             return;
         }
 
@@ -760,15 +786,15 @@
             if (isWorkspaceLocked()) {
                 sPendingAddList.add(args);
             } else {
-                delayExitSpringLoadedMode = completeAdd(args);
+                completeAdd(args);
             }
+            mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded,
+                    ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
         } else if (resultCode == RESULT_CANCELED) {
-            mWorkspace.stripEmptyScreens();
+            mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded,
+                    ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
         }
         mDragLayer.clearAnimatedView();
-        // Exit spring loaded mode if necessary after cancelling the configuration of a widget
-        exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), delayExitSpringLoadedMode,
-                null);
     }
 
     private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
@@ -788,25 +814,18 @@
                 public void run() {
                     completeAddAppWidget(appWidgetId, mPendingAddInfo.container,
                             mPendingAddInfo.screenId, layout, null);
-                    exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false,
-                            null);
+                    exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
+                            EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
                 }
             };
         } else if (resultCode == RESULT_CANCELED) {
             animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
-            onCompleteRunnable = new Runnable() {
-                @Override
-                public void run() {
-                    exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false,
-                            null);
-                }
-            };
         }
         if (mDragLayer.getAnimatedView() != null) {
             mWorkspace.animateWidgetDrop(mPendingAddInfo, cellLayout,
                     (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable,
                     animationType, boundWidget, true);
-        } else {
+        } else if (onCompleteRunnable != null) {
             // The animated view may be null in the case of a rotation during widget configuration
             onCompleteRunnable.run();
         }
@@ -1900,8 +1919,14 @@
         mPendingAddInfo.dropPos = null;
     }
 
-    void addAppWidgetImpl(final int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget,
-            AppWidgetProviderInfo appWidgetInfo) {
+    void addAppWidgetImpl(final int appWidgetId, final ItemInfo info,
+            final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo) {
+        addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0);
+    }
+
+    void addAppWidgetImpl(final int appWidgetId, final ItemInfo info,
+            final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo, int
+            delay) {
         if (appWidgetInfo.configure != null) {
             mPendingAddWidgetInfo = appWidgetInfo;
 
@@ -1912,10 +1937,17 @@
             Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_APPWIDGET);
         } else {
             // Otherwise just add it
+            Runnable onComplete = new Runnable() {
+                @Override
+                public void run() {
+                    // Exit spring loaded mode if necessary after adding the widget
+                    exitSpringLoadedDragModeDelayed(true, EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT,
+                            null);
+                }
+            };
             completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget,
                     appWidgetInfo);
-            // Exit spring loaded mode if necessary after adding the widget
-            exitSpringLoadedDragModeDelayed(true, false, null);
+            mWorkspace.removeExtraEmptyScreen(true, onComplete, delay, false);
         }
     }
 
@@ -2979,7 +3011,7 @@
 
             dispatchOnLauncherTransitionPrepare(fromView, animated, true);
             dispatchOnLauncherTransitionPrepare(toView, animated, true);
-            mAppsCustomizeContent.pauseScrolling();
+            mAppsCustomizeContent.stopScrolling();
 
             mStateAnimation.addListener(new AnimatorListenerAdapter() {
                 @Override
@@ -2991,7 +3023,6 @@
                         onCompleteRunnable.run();
                     }
                     mAppsCustomizeContent.updateCurrentPageScroll();
-                    mAppsCustomizeContent.resumeScrolling();
                 }
             });
 
@@ -3104,7 +3135,7 @@
         }
     }
 
-    void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay,
+    void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
             final Runnable onCompleteRunnable) {
         if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return;
 
@@ -3121,9 +3152,8 @@
                     exitSpringLoadedDragMode();
                 }
             }
-        }, (extendedDelay ?
-                EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT :
-                EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT));
+        }, delay);
+
     }
 
     void exitSpringLoadedDragMode() {
@@ -3662,7 +3692,7 @@
         }
 
         // Remove the extra empty screen
-        mWorkspace.removeExtraEmptyScreen();
+        mWorkspace.removeExtraEmptyScreen(false, null);
 
         if (!AppsCustomizePagedView.DISABLE_ALL_APPS &&
                 addedApps != null && mAppsCustomizeContent != null) {
@@ -3932,6 +3962,8 @@
         } else {
             if (mAppsCustomizeContent != null) {
                 mAppsCustomizeContent.setApps(apps);
+                mAppsCustomizeContent.onPackagesUpdated(
+                        LauncherModel.getSortedWidgetsAndShortcuts(this));
             }
         }
     }
@@ -3971,26 +4003,27 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public void bindComponentsRemoved(final ArrayList<String> packageNames,
-                                      final ArrayList<AppInfo> appInfos,
-                                      final boolean packageRemoved) {
+                                      final ArrayList<AppInfo> appInfos) {
         Runnable r = new Runnable() {
             public void run() {
-                bindComponentsRemoved(packageNames, appInfos, packageRemoved);
+                bindComponentsRemoved(packageNames, appInfos);
             }
         };
         if (waitUntilResume(r)) {
             return;
         }
 
-        if (packageRemoved) {
+        if (!packageNames.isEmpty()) {
             mWorkspace.removeItemsByPackageName(packageNames);
-        } else {
+        }
+        if (!appInfos.isEmpty()) {
             mWorkspace.removeItemsByApplicationInfo(appInfos);
         }
 
         // Notify the drag controller
-        mDragController.onAppsRemoved(appInfos, this);
+        mDragController.onAppsRemoved(packageNames, appInfos);
 
+        // Update AllApps
         if (!AppsCustomizePagedView.DISABLE_ALL_APPS &&
                 mAppsCustomizeContent != null) {
             mAppsCustomizeContent.removeApps(appInfos);
@@ -4007,7 +4040,6 @@
                 mWidgetsAndShortcuts = null;
             }
         };
-
     public void bindPackagesUpdated(final ArrayList<Object> widgetsAndShortcuts) {
         if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
             mWidgetsAndShortcuts = widgetsAndShortcuts;
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index 5d4f9c6..e6c220b 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -25,12 +25,13 @@
 import android.view.ViewTreeObserver;
 
 import java.util.HashSet;
+import java.util.WeakHashMap;
 
 public class LauncherAnimUtils {
-    static HashSet<Animator> sAnimators = new HashSet<Animator>();
+    static WeakHashMap<Animator, Object> sAnimators = new WeakHashMap<Animator, Object>();
     static Animator.AnimatorListener sEndAnimListener = new Animator.AnimatorListener() {
         public void onAnimationStart(Animator animation) {
-            sAnimators.add(animation);
+            sAnimators.put(animation, null);
         }
 
         public void onAnimationRepeat(Animator animation) {
@@ -74,13 +75,12 @@
     }
 
     public static void onDestroyActivity() {
-        HashSet<Animator> animators = new HashSet<Animator>(sAnimators);
+        HashSet<Animator> animators = new HashSet<Animator>(sAnimators.keySet());
         for (Animator a : animators) {
             if (a.isRunning()) {
                 a.cancel();
-            } else {
-                sAnimators.remove(a);
             }
+            sAnimators.remove(a);
         }
     }
 
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index a255b89..fe2b43f 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -28,7 +28,7 @@
 
 import java.lang.ref.WeakReference;
 
-public class LauncherAppState {
+public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
     private static final String TAG = "LauncherAppState";
     private static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
 
@@ -182,12 +182,12 @@
                     context.getResources(),
                     minWidth, minHeight, width, height,
                     availableWidth, availableHeight);
+            mDynamicGrid.getDeviceProfile().addCallback(this);
         }
 
         // Update the icon size
         DeviceProfile grid = mDynamicGrid.getDeviceProfile();
-        Utilities.setIconSize(grid.iconSizePx);
-        grid.updateFromConfiguration(context.getResources(), width, height,
+        grid.updateFromConfiguration(context, context.getResources(), width, height,
                 availableWidth, availableHeight);
         return grid;
     }
@@ -216,4 +216,9 @@
     public int getLongPressTimeout() {
         return mLongPressTimeout;
     }
+
+    @Override
+    public void onAvailableSizeChanged(DeviceProfile grid) {
+        Utilities.setIconSize(grid.iconSizePx);
+    }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 83aef1a..51a649a 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -65,6 +65,12 @@
     }
 
     public boolean onInterceptTouchEvent(MotionEvent ev) {
+        // Just in case the previous long press hasn't been cleared, we make sure to start fresh
+        // on touch down.
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mLongPressHelper.cancelLongPress();
+        }
+
         // Consume any touch events for ourselves after longpress is triggered
         if (mLongPressHelper.hasPerformedLongPress()) {
             mLongPressHelper.cancelLongPress();
@@ -110,13 +116,15 @@
 
     @Override
     public void onTouchComplete() {
-        mLongPressHelper.cancelLongPress();
+        if (!mLongPressHelper.hasPerformedLongPress()) {
+            // If a long press has been performed, we don't want to clear the record of that since
+            // we still may be receiving a touch up which we want to intercept
+            mLongPressHelper.cancelLongPress();
+        }
     }
 
     @Override
     public int getDescendantFocusability() {
         return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
     }
-
-
 }
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 9b901ee..8023fcd 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -31,9 +31,8 @@
 import com.android.launcher3.backup.BackupProtos.Widget;
 
 import android.app.backup.BackupDataInputStream;
-import android.app.backup.BackupHelper;
-import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupHelper;
 import android.app.backup.BackupManager;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 7e1442d..c746b4d 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -165,8 +165,7 @@
                                   ArrayList<AppInfo> addedApps);
         public void bindAppsUpdated(ArrayList<AppInfo> apps);
         public void bindComponentsRemoved(ArrayList<String> packageNames,
-                        ArrayList<AppInfo> appInfos,
-                        boolean matchPackageNamesOnly);
+                        ArrayList<AppInfo> appInfos);
         public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
         public void bindSearchablesChanged();
         public boolean isAllAppsButtonRank(int rank);
@@ -186,9 +185,6 @@
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
         mIconCache = iconCache;
 
-        mDefaultIcon = Utilities.createIconBitmap(
-                mIconCache.getFullResDefaultActivityIcon(), context);
-
         final Resources res = context.getResources();
         Configuration config = res.getConfiguration();
         mPreviousConfigMcc = config.mcc;
@@ -400,6 +396,11 @@
     }
 
     public Bitmap getFallbackIcon() {
+        if (mDefaultIcon == null) {
+            final Context context = LauncherAppState.getInstance().getContext();
+            mDefaultIcon = Utilities.createIconBitmap(
+                    mIconCache.getFullResDefaultActivityIcon(), context);
+        }
         return Bitmap.createBitmap(mDefaultIcon);
     }
 
@@ -2551,43 +2552,47 @@
                     }
                 });
             }
-            // If a package has been removed, or an app has been removed as a result of
-            // an update (for example), make the removed callback.
-            if (mOp == OP_REMOVE || !removedApps.isEmpty()) {
-                final boolean packageRemoved = (mOp == OP_REMOVE);
-                final ArrayList<String> removedPackageNames =
-                        new ArrayList<String>(Arrays.asList(packages));
 
-                // Update the launcher db to reflect the removal of apps
-                if (packageRemoved) {
-                    for (String pn : removedPackageNames) {
-                        ArrayList<ItemInfo> infos = getItemInfoForPackageName(pn);
-                        for (ItemInfo i : infos) {
-                            deleteItemFromDatabase(context, i);
-                        }
-                    }
-
-                    // Remove any queued items from the install queue
-                    String spKey = LauncherAppState.getSharedPreferencesKey();
-                    SharedPreferences sp =
-                            context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
-                    InstallShortcutReceiver.removeFromInstallQueue(sp, removedPackageNames);
-                } else {
-                    for (AppInfo a : removedApps) {
-                        ArrayList<ItemInfo> infos =
-                                getItemInfoForComponentName(a.componentName);
-                        for (ItemInfo i : infos) {
-                            deleteItemFromDatabase(context, i);
-                        }
+            final ArrayList<String> removedPackageNames =
+                    new ArrayList<String>();
+            if (mOp == OP_REMOVE) {
+                // Mark all packages in the broadcast to be removed
+                removedPackageNames.addAll(Arrays.asList(packages));
+            } else if (mOp == OP_UPDATE) {
+                // Mark disabled packages in the broadcast to be removed
+                final PackageManager pm = context.getPackageManager();
+                for (int i=0; i<N; i++) {
+                    if (isPackageDisabled(pm, packages[i])) {
+                        removedPackageNames.add(packages[i]);
                     }
                 }
-
+            }
+            // Remove all the components associated with this package
+            for (String pn : removedPackageNames) {
+                ArrayList<ItemInfo> infos = getItemInfoForPackageName(pn);
+                for (ItemInfo i : infos) {
+                    deleteItemFromDatabase(context, i);
+                }
+            }
+            // Remove all the specific components
+            for (AppInfo a : removedApps) {
+                ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName);
+                for (ItemInfo i : infos) {
+                    deleteItemFromDatabase(context, i);
+                }
+            }
+            if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) {
+                // Remove any queued items from the install queue
+                String spKey = LauncherAppState.getSharedPreferencesKey();
+                SharedPreferences sp =
+                        context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
+                InstallShortcutReceiver.removeFromInstallQueue(sp, removedPackageNames);
+                // Call the components-removed callback
                 mHandler.post(new Runnable() {
                     public void run() {
                         Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
                         if (callbacks == cb && cb != null) {
-                            callbacks.bindComponentsRemoved(removedPackageNames,
-                                    removedApps, packageRemoved);
+                            callbacks.bindComponentsRemoved(removedPackageNames, removedApps);
                         }
                     }
                 });
@@ -2629,19 +2634,26 @@
         return widgetsAndShortcuts;
     }
 
+    private boolean isPackageDisabled(PackageManager pm, String packageName) {
+        try {
+            PackageInfo pi = pm.getPackageInfo(packageName, 0);
+            return !pi.applicationInfo.enabled;
+        } catch (NameNotFoundException e) {
+            // Fall through
+        }
+        return false;
+    }
     private boolean isValidPackageComponent(PackageManager pm, ComponentName cn) {
         if (cn == null) {
             return false;
         }
+        if (isPackageDisabled(pm, cn.getPackageName())) {
+            return false;
+        }
 
         try {
-            // Skip if the application is disabled
-            PackageInfo pi = pm.getPackageInfo(cn.getPackageName(), 0);
-            if (!pi.applicationInfo.enabled) {
-                return false;
-            }
-
             // Check the activity
+            PackageInfo pi = pm.getPackageInfo(cn.getPackageName(), 0);
             return (pm.getActivityInfo(cn, 0) != null);
         } catch (NameNotFoundException e) {
             return false;
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index e982985..46c861b 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -124,6 +124,7 @@
     protected int mMaxScrollX;
     protected Scroller mScroller;
     private VelocityTracker mVelocityTracker;
+    private int mPageSpacing = 0;
 
     private float mParentDownMotionX;
     private float mParentDownMotionY;
@@ -157,7 +158,6 @@
     protected int mTouchSlop;
     private int mPagingTouchSlop;
     private int mMaximumVelocity;
-    protected int mPageSpacing;
     protected int mPageLayoutPaddingTop;
     protected int mPageLayoutPaddingBottom;
     protected int mPageLayoutPaddingLeft;
@@ -260,16 +260,11 @@
     // Drop to delete
     private View mDeleteDropTarget;
 
-    private boolean mAutoComputePageSpacing = false;
-    private boolean mRecomputePageSpacing = false;
-
     // Bouncer
     private boolean mTopAlignPageWhenShrinkingForBouncer = false;
 
     protected final Rect mInsets = new Rect();
 
-    protected int mFirstChildLeft;
-
     public interface PageSwitchListener {
         void onPageSwitch(View newPage, int newPageIndex);
     }
@@ -287,10 +282,7 @@
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.PagedView, defStyle, 0);
-        setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0));
-        if (mPageSpacing < 0) {
-            mAutoComputePageSpacing = mRecomputePageSpacing = true;
-        }
+
         mPageLayoutPaddingTop = a.getDimensionPixelSize(
                 R.styleable.PagedView_pageLayoutPaddingTop, 0);
         mPageLayoutPaddingBottom = a.getDimensionPixelSize(
@@ -515,33 +507,41 @@
         }
         scrollTo(newX, 0);
         mScroller.setFinalX(newX);
-        mScroller.forceFinished(true);
+        forceFinishScroller();
     }
 
     /**
      * Called during AllApps/Home transitions to avoid unnecessary work. When that other animation
-     * ends, {@link #resumeScrolling()} should be called, along with
-     * {@link #updateCurrentPageScroll()} to correctly set the final state and re-enable scrolling.
+     * {@link #updateCurrentPageScroll()} should be called, to correctly set the final state and
+     * re-enable scrolling.
      */
-    void pauseScrolling() {
-        mScroller.forceFinished(true);
+    void stopScrolling() {
+        mCurrentPage = getNextPage();
+        forceFinishScroller();
     }
 
-    /**
-     * Enables scrolling again.
-     * @see #pauseScrolling()
-     */
-    void resumeScrolling() {
+    private void abortScrollerAnimation(boolean resetNextPage) {
+        mScroller.abortAnimation();
+        // We need to clean up the next page here to avoid computeScrollHelper from
+        // updating current page on the pass.
+        if (resetNextPage) {
+            mNextPage = INVALID_PAGE;
+        }
     }
+
+    private void forceFinishScroller() {
+        mScroller.forceFinished(true);
+        // We need to clean up the next page here to avoid computeScrollHelper from
+        // updating current page on the pass.
+        mNextPage = INVALID_PAGE;
+    }
+
     /**
      * Sets the current page.
      */
     void setCurrentPage(int currentPage) {
         if (!mScroller.isFinished()) {
-            mScroller.abortAnimation();
-            // We need to clean up the next page here to avoid computeScrollHelper from
-            // updating current page on the pass.
-            mNextPage = INVALID_PAGE;
+            abortScrollerAnimation(true);
         }
         // don't introduce any checks like mCurrentPage == currentPage here-- if we change the
         // the default
@@ -675,6 +675,7 @@
                     AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
             ev.setItemCount(getChildCount());
             ev.setFromIndex(mCurrentPage);
+            ev.setToIndex(getNextPage());
 
             final int action;
             if (getNextPage() >= mCurrentPage) {
@@ -786,13 +787,13 @@
         // NOTE: We multiply by 1.5f to account for the fact that depending on the offset of the
         // viewport, we can be at most one and a half screens offset once we scale down
         DisplayMetrics dm = getResources().getDisplayMetrics();
-        int maxSize = Math.max(dm.widthPixels, dm.heightPixels + mInsets.top + mInsets.bottom);
+        int maxSize = Math.max(dm.widthPixels + mInsets.left + mInsets.right,
+                dm.heightPixels + mInsets.top + mInsets.bottom);
 
-        int parentWidthSize, parentHeightSize;
+        int parentWidthSize = (int) (1.5f * maxSize);
+        int parentHeightSize = maxSize;
         int scaledWidthSize, scaledHeightSize;
         if (mUseMinScale) {
-            parentWidthSize = (int) (1.5f * maxSize);
-            parentHeightSize = maxSize;
             scaledWidthSize = (int) (parentWidthSize / mMinScale);
             scaledHeightSize = (int) (parentHeightSize / mMinScale);
         } else {
@@ -852,21 +853,17 @@
                         childHeightMode = MeasureSpec.EXACTLY;
                     }
 
-                    childWidth = widthSize - horizontalPadding;
-                    childHeight = heightSize - verticalPadding - mInsets.top - mInsets.bottom;
+                    childWidth = getViewportWidth() - horizontalPadding
+                            - mInsets.left - mInsets.right;
+                    childHeight = getViewportHeight() - verticalPadding
+                            - mInsets.top - mInsets.bottom;
                     mNormalChildHeight = childHeight;
-
                 } else {
                     childWidthMode = MeasureSpec.EXACTLY;
                     childHeightMode = MeasureSpec.EXACTLY;
 
-                    if (mUseMinScale) {
-                        childWidth = getViewportWidth();
-                        childHeight = getViewportHeight();
-                    } else {
-                        childWidth = widthSize - getPaddingLeft() - getPaddingRight();
-                        childHeight = heightSize - getPaddingTop() - getPaddingBottom();
-                    }
+                    childWidth = getViewportWidth() - mInsets.left - mInsets.right;
+                    childHeight = getViewportHeight();
                 }
 
                 final int childWidthMeasureSpec =
@@ -877,30 +874,6 @@
             }
         }
         setMeasuredDimension(scaledWidthSize, scaledHeightSize);
-
-        if (childCount > 0) {
-            // Calculate the variable page spacing if necessary
-            if (mAutoComputePageSpacing && mRecomputePageSpacing) {
-                // The gap between pages in the PagedView should be equal to the gap from the page
-                // to the edge of the screen (so it is not visible in the current screen).  To
-                // account for unequal padding on each side of the paged view, we take the maximum
-                // of the left/right gap and use that as the gap between each page.
-                int offset = (getViewportWidth() - getChildWidth(0)) / 2;
-                int spacing = Math.max(offset, widthSize - offset -
-                        getChildAt(0).getMeasuredWidth());
-                setPageSpacing(spacing);
-                mRecomputePageSpacing = false;
-            }
-        }
-    }
-
-    public void setPageSpacing(int pageSpacing) {
-        mPageSpacing = pageSpacing;
-        requestLayout();
-    }
-
-    protected int getFirstChildLeft() {
-        return mFirstChildLeft;
     }
 
     @Override
@@ -912,8 +885,6 @@
         if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
         final int childCount = getChildCount();
 
-        int screenWidth = getViewportWidth();
-
         int offsetX = getViewportOffsetX();
         int offsetY = getViewportOffsetY();
 
@@ -928,7 +899,9 @@
 
         int verticalPadding = getPaddingTop() + getPaddingBottom();
 
-        int childLeft = mFirstChildLeft = offsetX + (screenWidth - getChildWidth(startIndex)) / 2;
+        LayoutParams lp = (LayoutParams) getChildAt(startIndex).getLayoutParams();
+
+        int childLeft = offsetX + (lp.isFullScreenPage ? 0 : getPaddingLeft());
         if (mPageScrolls == null || getChildCount() != mChildCountOnLastLayout) {
             mPageScrolls = new int[getChildCount()];
         }
@@ -936,7 +909,7 @@
         for (int i = startIndex; i != endIndex; i += delta) {
             final View child = getPageAt(i);
             if (child.getVisibility() != View.GONE) {
-                LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                lp = (LayoutParams) child.getLayoutParams();
                 int childTop;
                 if (lp.isFullScreenPage) {
                     childTop = offsetY;
@@ -954,16 +927,9 @@
                 child.layout(childLeft, childTop,
                         childLeft + child.getMeasuredWidth(), childTop + childHeight);
 
-                // We assume the left and right padding are equal, and hence center the pages
-                // horizontally
-                int scrollOffset = (getViewportWidth() - childWidth) / 2;
-                mPageScrolls[i] = childLeft - scrollOffset - offsetX;
-
-                if (i != endIndex - delta) {
-                    childLeft += childWidth + scrollOffset;
-                    int nextScrollOffset = (getViewportWidth() - getChildWidth(i + delta)) / 2;
-                    childLeft += nextScrollOffset;
-                }
+                int scrollOffsetLeft = lp.isFullScreenPage ? 0 : getPaddingLeft();
+                mPageScrolls[i] = childLeft - scrollOffsetLeft - offsetX;
+                childLeft += childWidth + mPageSpacing;
             }
         }
 
@@ -997,6 +963,11 @@
         }
     }
 
+    public void setPageSpacing(int pageSpacing) {
+        mPageSpacing = pageSpacing;
+        requestLayout();
+    }
+
     protected void screenScrolled(int screenCenter) {
         boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
 
@@ -1035,7 +1006,6 @@
         // This ensures that when children are added, they get the correct transforms / alphas
         // in accordance with any scroll effects.
         mForceScreenScrolled = true;
-        mRecomputePageSpacing = true;
         updateFreescrollBounds();
         invalidate();
     }
@@ -1304,24 +1274,22 @@
      * Return true if a tap at (x, y) should trigger a flip to the previous page.
      */
     protected boolean hitsPreviousPage(float x, float y) {
-        int offset = (getViewportWidth() - getChildWidth(mCurrentPage)) / 2;
         if (isLayoutRtl()) {
             return (x > (getViewportOffsetX() + getViewportWidth() -
-                    offset + mPageSpacing));
+                    getPaddingRight() - mPageSpacing));
         }
-        return (x < getViewportOffsetX() + offset - mPageSpacing);
+        return (x < getViewportOffsetX() + getPaddingLeft() + mPageSpacing);
     }
 
     /**
      * Return true if a tap at (x, y) should trigger a flip to the next page.
      */
     protected boolean hitsNextPage(float x, float y) {
-        int offset = (getViewportWidth() - getChildWidth(mCurrentPage)) / 2;
         if (isLayoutRtl()) {
-            return (x < getViewportOffsetX() + offset - mPageSpacing);
+            return (x < getViewportOffsetX() + getPaddingLeft() + mPageSpacing);
         }
         return  (x > (getViewportOffsetX() + getViewportWidth() -
-                offset + mPageSpacing));
+                getPaddingRight() - mPageSpacing));
     }
 
     /** Returns whether x and y originated within the buffered viewport */
@@ -1400,7 +1368,7 @@
                 final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
                 if (finishedScrolling) {
                     mTouchState = TOUCH_STATE_REST;
-                    mScroller.abortAnimation();
+                    abortScrollerAnimation(false);
                 } else {
                     if (isTouchPointInViewportWithBuffer((int) mDownMotionX, (int) mDownMotionY)) {
                         mTouchState = TOUCH_STATE_SCROLLING;
@@ -1512,8 +1480,21 @@
     protected float getScrollProgress(int screenCenter, View v, int page) {
         final int halfScreenSize = getViewportWidth() / 2;
 
-        int totalDistance = v.getMeasuredWidth() + mPageSpacing;
         int delta = screenCenter - (getScrollForPage(page) + halfScreenSize);
+        int count = getChildCount();
+
+        final int totalDistance;
+
+        int adjacentPage = page + 1;
+        if ((delta < 0 && !isLayoutRtl()) || (delta > 0 && isLayoutRtl())) {
+            adjacentPage = page - 1;
+        }
+
+        if (adjacentPage < 0 || adjacentPage > count - 1) {
+            totalDistance = v.getMeasuredWidth() + mPageSpacing;
+        } else {
+            totalDistance = Math.abs(getScrollForPage(adjacentPage) - getScrollForPage(page));
+        }
 
         float scrollProgress = delta / (totalDistance * 1.0f);
         scrollProgress = Math.min(scrollProgress, getMaxScrollProgress());
@@ -1536,7 +1517,13 @@
             return 0;
         } else {
             View child = getChildAt(index);
-            int scrollOffset = (getViewportWidth() - child.getMeasuredWidth()) / 2;
+
+            int scrollOffset = 0;
+            LayoutParams lp = (LayoutParams) child.getLayoutParams();
+            if (!lp.isFullScreenPage) {
+                scrollOffset = isLayoutRtl() ? getPaddingRight() : getPaddingLeft();
+            }
+
             int baselineX = mPageScrolls[index] + scrollOffset + getViewportOffsetX();
             return (int) (child.getX() - baselineX);
         }
@@ -1698,7 +1685,7 @@
              * will be false if being flinged.
              */
             if (!mScroller.isFinished()) {
-                mScroller.abortAnimation();
+                abortScrollerAnimation(false);
             }
 
             // Remember where the motion event started
@@ -1896,7 +1883,7 @@
                     }
                 } else {
                     if (!mScroller.isFinished()) {
-                        mScroller.abortAnimation();
+                        abortScrollerAnimation(true);
                     }
 
                     float scaleX = getScaleX();
@@ -2360,8 +2347,7 @@
 
         if (mContentIsRefreshable) {
             // Force all scrolling-related behavior to end
-            mScroller.forceFinished(true);
-            mNextPage = INVALID_PAGE;
+            forceFinishScroller();
 
             // Update all the pages
             syncPages();
diff --git a/src/com/android/launcher3/PagedViewIcon.java b/src/com/android/launcher3/PagedViewIcon.java
index 8bfe42d..865c0b2 100644
--- a/src/com/android/launcher3/PagedViewIcon.java
+++ b/src/com/android/launcher3/PagedViewIcon.java
@@ -62,15 +62,19 @@
         // Ensure we are using the right text size
         LauncherAppState app = LauncherAppState.getInstance();
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.iconTextSize);
+        setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
     }
 
     public void applyFromApplicationInfo(AppInfo info, boolean scaleUp,
             PagedViewIcon.PressedCallback cb) {
+        LauncherAppState app = LauncherAppState.getInstance();
+        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+
         mIcon = info.iconBitmap;
         mPressedCallback = cb;
         setCompoundDrawables(null, Utilities.createIconDrawable(mIcon),
                 null, null);
+        setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
         setText(info.title);
         setTag(info);
     }
diff --git a/src/com/android/launcher3/PagedViewWidget.java b/src/com/android/launcher3/PagedViewWidget.java
index 45320a4..d2f897a 100644
--- a/src/com/android/launcher3/PagedViewWidget.java
+++ b/src/com/android/launcher3/PagedViewWidget.java
@@ -84,11 +84,11 @@
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
         TextView name = (TextView) findViewById(R.id.widget_name);
         if (name != null) {
-            name.setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.iconTextSize);
+            name.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
         }
         TextView dims = (TextView) findViewById(R.id.widget_dims);
         if (dims != null) {
-            dims.setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.iconTextSize);
+            dims.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
         }
     }
 
diff --git a/src/com/android/launcher3/SavedWallpaperImages.java b/src/com/android/launcher3/SavedWallpaperImages.java
index 8d5b005..58add70 100644
--- a/src/com/android/launcher3/SavedWallpaperImages.java
+++ b/src/com/android/launcher3/SavedWallpaperImages.java
@@ -60,12 +60,9 @@
         public void onClick(WallpaperPickerActivity a) {
             String imageFilename = a.getSavedImages().getImageFilename(mDbId);
             File file = new File(a.getFilesDir(), imageFilename);
-            CropView v = a.getCropView();
-            int rotation = WallpaperCropActivity.getRotationFromExif(file.getAbsolutePath());
-            v.setTileSource(
-                    new BitmapRegionTileSource(a, file.getAbsolutePath(), 1024, rotation), null);
-            v.moveToLeft();
-            v.setTouchEnabled(false);
+            BitmapRegionTileSource.FilePathBitmapSource bitmapSource =
+                    new BitmapRegionTileSource.FilePathBitmapSource(file.getAbsolutePath(), 1024);
+            a.setCropViewTileSource(bitmapSource, false, true, null);
         }
         @Override
         public void onSave(WallpaperPickerActivity a) {
diff --git a/src/com/android/launcher3/SmoothPagedView.java b/src/com/android/launcher3/SmoothPagedView.java
index a45dbbf..64dcb34 100644
--- a/src/com/android/launcher3/SmoothPagedView.java
+++ b/src/com/android/launcher3/SmoothPagedView.java
@@ -52,8 +52,6 @@
         }
 
         public float getInterpolation(float t) {
-            // _o(t) = t * t * ((tension + 1) * t + tension)
-            // o(t) = _o(t - 1) + 1
             t -= 1.0f;
             return t * t * ((mTension + 1) * t + mTension) + 1.0f;
         }
diff --git a/src/com/android/launcher3/WallpaperCropActivity.java b/src/com/android/launcher3/WallpaperCropActivity.java
index 30ec340..276aba3 100644
--- a/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/src/com/android/launcher3/WallpaperCropActivity.java
@@ -37,15 +37,16 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
-import android.util.FloatMath;
 import android.util.Log;
 import android.view.Display;
 import android.view.View;
 import android.view.WindowManager;
+import android.widget.Toast;
 
 import com.android.gallery3d.common.Utils;
 import com.android.gallery3d.exif.ExifInterface;
 import com.android.photos.BitmapRegionTileSource;
+import com.android.photos.BitmapRegionTileSource.BitmapSource;
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
@@ -96,9 +97,6 @@
             return;
         }
 
-        int rotation = getRotationFromExif(this, imageUri);
-        mCropView.setTileSource(new BitmapRegionTileSource(this, imageUri, 1024, rotation), null);
-        mCropView.setTouchEnabled(true);
         // Action bar
         // Show the custom action bar view
         final ActionBar actionBar = getActionBar();
@@ -111,6 +109,63 @@
                         cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
                     }
                 });
+
+        // Load image in background
+        final BitmapRegionTileSource.UriBitmapSource bitmapSource =
+                new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024);
+        Runnable onLoad = new Runnable() {
+            public void run() {
+                if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) {
+                    Toast.makeText(WallpaperCropActivity.this,
+                            getString(R.string.wallpaper_load_fail),
+                            Toast.LENGTH_LONG).show();
+                    finish();
+                }
+            }
+        };
+        setCropViewTileSource(bitmapSource, true, false, onLoad);
+    }
+
+    public void setCropViewTileSource(
+            final BitmapRegionTileSource.BitmapSource bitmapSource, final boolean touchEnabled,
+            final boolean moveToLeft, final Runnable postExecute) {
+        final Context context = WallpaperCropActivity.this;
+        final View progressView = findViewById(R.id.loading);
+        final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
+            protected Void doInBackground(Void...args) {
+                if (!isCancelled()) {
+                    bitmapSource.loadInBackground();
+                }
+                return null;
+            }
+            protected void onPostExecute(Void arg) {
+                if (!isCancelled()) {
+                    progressView.setVisibility(View.INVISIBLE);
+                    if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
+                        mCropView.setTileSource(
+                                new BitmapRegionTileSource(context, bitmapSource), null);
+                        mCropView.setTouchEnabled(touchEnabled);
+                        if (moveToLeft) {
+                            mCropView.moveToLeft();
+                        }
+                    }
+                }
+                if (postExecute != null) {
+                    postExecute.run();
+                }
+            }
+        };
+        // We don't want to show the spinner every time we load an image, because that would be
+        // annoying; instead, only start showing the spinner if loading the image has taken
+        // longer than 1 sec (ie 1000 ms)
+        progressView.postDelayed(new Runnable() {
+            public void run() {
+                if (loadBitmapTask.getStatus() != AsyncTask.Status.FINISHED) {
+                    progressView.setVisibility(View.VISIBLE);
+                }
+            }
+        }, 1000);
+        loadBitmapTask.execute();
     }
 
     public boolean enableRotation() {
@@ -192,16 +247,18 @@
     private static int getRotationFromExifHelper(
             String path, Resources res, int resId, Context context, Uri uri) {
         ExifInterface ei = new ExifInterface();
+        InputStream is = null;
+        BufferedInputStream bis = null;
         try {
             if (path != null) {
                 ei.readExif(path);
             } else if (uri != null) {
-                InputStream is = context.getContentResolver().openInputStream(uri);
-                BufferedInputStream bis = new BufferedInputStream(is);
+                is = context.getContentResolver().openInputStream(uri);
+                bis = new BufferedInputStream(is);
                 ei.readExif(bis);
             } else {
-                InputStream is = res.openRawResource(resId);
-                BufferedInputStream bis = new BufferedInputStream(is);
+                is = res.openRawResource(resId);
+                bis = new BufferedInputStream(is);
                 ei.readExif(bis);
             }
             Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
@@ -210,6 +267,9 @@
             }
         } catch (IOException e) {
             Log.w(LOGTAG, "Getting exif data failed", e);
+        } finally {
+            Utils.closeSilently(bis);
+            Utils.closeSilently(is);
         }
         return 0;
     }
@@ -269,40 +329,15 @@
         // Get the crop
         boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
 
-        Point minDims = new Point();
-        Point maxDims = new Point();
+
         Display d = getWindowManager().getDefaultDisplay();
-        d.getCurrentSizeRange(minDims, maxDims);
 
         Point displaySize = new Point();
         d.getSize(displaySize);
-
-        int maxDim = Math.max(maxDims.x, maxDims.y);
-        final int minDim = Math.min(minDims.x, minDims.y);
-        int defaultWallpaperWidth;
-        if (isScreenLarge(getResources())) {
-            defaultWallpaperWidth = (int) (maxDim *
-                    wallpaperTravelToScreenWidthRatio(maxDim, minDim));
-        } else {
-            defaultWallpaperWidth = Math.max((int)
-                    (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
-        }
-
         boolean isPortrait = displaySize.x < displaySize.y;
-        int portraitHeight;
-        if (isPortrait) {
-            portraitHeight = mCropView.getHeight();
-        } else {
-            // TODO: how to actually get the proper portrait height?
-            // This is not quite right:
-            portraitHeight = Math.max(maxDims.x, maxDims.y);
-        }
-        if (android.os.Build.VERSION.SDK_INT >=
-                android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            Point realSize = new Point();
-            d.getRealSize(realSize);
-            portraitHeight = Math.max(realSize.x, realSize.y);
-        }
+
+        Point defaultWallpaperSize = getDefaultWallpaperSize(getResources(),
+                getWindowManager());
         // Get the crop
         RectF cropRect = mCropView.getCrop();
         int cropRotation = mCropView.getImageRotation();
@@ -321,7 +356,7 @@
         // (or all the way to the left, in RTL)
         float extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left;
         // Cap the amount of extra width
-        float maxExtraSpace = defaultWallpaperWidth / cropScale - cropRect.width();
+        float maxExtraSpace = defaultWallpaperSize.x / cropScale - cropRect.width();
         extraSpace = Math.min(extraSpace, maxExtraSpace);
 
         if (ltr) {
@@ -332,10 +367,10 @@
 
         // ADJUST CROP HEIGHT
         if (isPortrait) {
-            cropRect.bottom = cropRect.top + portraitHeight / cropScale;
+            cropRect.bottom = cropRect.top + defaultWallpaperSize.y / cropScale;
         } else { // LANDSCAPE
             float extraPortraitHeight =
-                    portraitHeight / cropScale - cropRect.height();
+                    defaultWallpaperSize.y / cropScale - cropRect.height();
             float expandHeight =
                     Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top),
                             extraPortraitHeight / 2);
@@ -372,7 +407,6 @@
         String mInFilePath;
         byte[] mInImageBytes;
         int mInResId = 0;
-        InputStream mInStream;
         RectF mCropBounds = null;
         int mOutWidth, mOutHeight;
         int mRotation;
@@ -445,37 +479,36 @@
         }
 
         // Helper to setup input stream
-        private void regenerateInputStream() {
+        private InputStream regenerateInputStream() {
             if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) {
                 Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " +
                         "image byte array given");
             } else {
-                Utils.closeSilently(mInStream);
                 try {
                     if (mInUri != null) {
-                        mInStream = new BufferedInputStream(
+                        return new BufferedInputStream(
                                 mContext.getContentResolver().openInputStream(mInUri));
                     } else if (mInFilePath != null) {
-                        mInStream = mContext.openFileInput(mInFilePath);
+                        return mContext.openFileInput(mInFilePath);
                     } else if (mInImageBytes != null) {
-                        mInStream = new BufferedInputStream(
-                                new ByteArrayInputStream(mInImageBytes));
+                        return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes));
                     } else {
-                        mInStream = new BufferedInputStream(
-                                mResources.openRawResource(mInResId));
+                        return new BufferedInputStream(mResources.openRawResource(mInResId));
                     }
                 } catch (FileNotFoundException e) {
                     Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e);
                 }
             }
+            return null;
         }
 
         public Point getImageBounds() {
-            regenerateInputStream();
-            if (mInStream != null) {
+            InputStream is = regenerateInputStream();
+            if (is != null) {
                 BitmapFactory.Options options = new BitmapFactory.Options();
                 options.inJustDecodeBounds = true;
-                BitmapFactory.decodeStream(mInStream, null, options);
+                BitmapFactory.decodeStream(is, null, options);
+                Utils.closeSilently(is);
                 if (options.outWidth != 0 && options.outHeight != 0) {
                     return new Point(options.outWidth, options.outHeight);
                 }
@@ -493,22 +526,26 @@
         public boolean cropBitmap() {
             boolean failure = false;
 
-            regenerateInputStream();
 
             WallpaperManager wallpaperManager = null;
             if (mSetWallpaper) {
                 wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
             }
-            if (mSetWallpaper && mNoCrop && mInStream != null) {
+
+
+            if (mSetWallpaper && mNoCrop) {
                 try {
-                    wallpaperManager.setStream(mInStream);
+                    InputStream is = regenerateInputStream();
+                    if (is != null) {
+                        wallpaperManager.setStream(is);
+                        Utils.closeSilently(is);
+                    }
                 } catch (IOException e) {
                     Log.w(LOGTAG, "cannot write stream to wallpaper", e);
                     failure = true;
                 }
                 return !failure;
-            }
-            if (mInStream != null) {
+            } else {
                 // Find crop bounds (scaled to original image size)
                 Rect roundedTrueCrop = new Rect();
                 Matrix rotateMatrix = new Matrix();
@@ -521,6 +558,11 @@
                     mCropBounds = new RectF(roundedTrueCrop);
 
                     Point bounds = getImageBounds();
+                    if (bounds == null) {
+                        Log.w(LOGTAG, "cannot get bounds for image");
+                        failure = true;
+                        return false;
+                    }
 
                     float[] rotatedBounds = new float[] { bounds.x, bounds.y };
                     rotateMatrix.mapPoints(rotatedBounds);
@@ -531,7 +573,6 @@
                     inverseRotateMatrix.mapRect(mCropBounds);
                     mCropBounds.offset(bounds.x/2, bounds.y/2);
 
-                    regenerateInputStream();
                 }
 
                 mCropBounds.roundOut(roundedTrueCrop);
@@ -543,15 +584,25 @@
                 }
 
                 // See how much we're reducing the size of the image
-                int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / mOutWidth,
-                        roundedTrueCrop.height() / mOutHeight);
-
+                int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth,
+                        roundedTrueCrop.height() / mOutHeight));
                 // Attempt to open a region decoder
                 BitmapRegionDecoder decoder = null;
+                InputStream is = null;
                 try {
-                    decoder = BitmapRegionDecoder.newInstance(mInStream, true);
+                    is = regenerateInputStream();
+                    if (is == null) {
+                        Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
+                        failure = true;
+                        return false;
+                    }
+                    decoder = BitmapRegionDecoder.newInstance(is, false);
+                    Utils.closeSilently(is);
                 } catch (IOException e) {
                     Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
+                } finally {
+                   Utils.closeSilently(is);
+                   is = null;
                 }
 
                 Bitmap crop = null;
@@ -567,14 +618,15 @@
 
                 if (crop == null) {
                     // BitmapRegionDecoder has failed, try to crop in-memory
-                    regenerateInputStream();
+                    is = regenerateInputStream();
                     Bitmap fullSize = null;
-                    if (mInStream != null) {
+                    if (is != null) {
                         BitmapFactory.Options options = new BitmapFactory.Options();
                         if (scaleDownSampleSize > 1) {
                             options.inSampleSize = scaleDownSampleSize;
                         }
-                        fullSize = BitmapFactory.decodeStream(mInStream, null, options);
+                        fullSize = BitmapFactory.decodeStream(is, null, options);
+                        Utils.closeSilently(is);
                     }
                     if (fullSize != null) {
                         mCropBounds.left /= scaleDownSampleSize;
@@ -686,7 +738,7 @@
 
     protected void updateWallpaperDimensions(int width, int height) {
         String spKey = getSharedPreferencesKey();
-        SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
+        SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
         SharedPreferences.Editor editor = sp.edit();
         if (width != 0 && height != 0) {
             editor.putInt(WALLPAPER_WIDTH_KEY, width);
diff --git a/src/com/android/launcher3/WallpaperPickerActivity.java b/src/com/android/launcher3/WallpaperPickerActivity.java
index e71a26b..efc3110 100644
--- a/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -31,7 +31,9 @@
 import android.database.DataSetObserver;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
 import android.graphics.Matrix;
+import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
@@ -40,6 +42,8 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LevelListDrawable;
 import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Bundle;
 import android.provider.MediaStore;
 import android.util.Log;
@@ -51,10 +55,10 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.BaseAdapter;
@@ -63,15 +67,14 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListAdapter;
+import android.widget.Toast;
 
-import com.android.gallery3d.exif.ExifInterface;
 import com.android.photos.BitmapRegionTileSource;
+import com.android.photos.BitmapRegionTileSource.BitmapSource;
 
-import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.ArrayList;
 
 public class WallpaperPickerActivity extends WallpaperCropActivity {
@@ -81,8 +84,9 @@
     public static final int PICK_WALLPAPER_THIRD_PARTY_ACTIVITY = 6;
     public static final int PICK_LIVE_WALLPAPER = 7;
     private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES";
+    private static final String DEFAULT_WALLPAPER_THUMBNAIL_FILENAME = "default_thumb.jpg";
 
-    private View mSelectedThumb;
+    private View mSelectedTile;
     private boolean mIgnoreNextTap;
     private OnClickListener mThumbnailOnClickListener;
 
@@ -126,15 +130,39 @@
 
     public static class UriWallpaperInfo extends WallpaperTileInfo {
         private Uri mUri;
+        private boolean mFirstClick = true;
+        private BitmapRegionTileSource.UriBitmapSource mBitmapSource;
         public UriWallpaperInfo(Uri uri) {
             mUri = uri;
         }
         @Override
-        public void onClick(WallpaperPickerActivity a) {
-            CropView v = a.getCropView();
-            int rotation = WallpaperCropActivity.getRotationFromExif(a, mUri);
-            v.setTileSource(new BitmapRegionTileSource(a, mUri, 1024, rotation), null);
-            v.setTouchEnabled(true);
+        public void onClick(final WallpaperPickerActivity a) {
+            final Runnable onLoad;
+            if (!mFirstClick) {
+                onLoad = null;
+            } else {
+                mFirstClick = false;
+                onLoad = new Runnable() {
+                    public void run() {
+                        if (mBitmapSource != null &&
+                                mBitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
+                            mView.setVisibility(View.VISIBLE);
+                            a.selectTile(mView);
+                        } else {
+                            ViewGroup parent = (ViewGroup) mView.getParent();
+                            if (parent != null) {
+                                parent.removeView(mView);
+                                Toast.makeText(a,
+                                        a.getString(R.string.image_load_fail),
+                                        Toast.LENGTH_SHORT).show();
+                            }
+                        }
+                    }
+                };
+            }
+            mBitmapSource = new BitmapRegionTileSource.UriBitmapSource(
+                    a, mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE);
+            a.setCropViewTileSource(mBitmapSource, true, false, onLoad);
         }
         @Override
         public void onSave(final WallpaperPickerActivity a) {
@@ -172,9 +200,11 @@
         }
         @Override
         public void onClick(WallpaperPickerActivity a) {
-            int rotation = WallpaperCropActivity.getRotationFromExif(mResources, mResId);
-            BitmapRegionTileSource source = new BitmapRegionTileSource(
-                    mResources, a, mResId, 1024, rotation);
+            BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
+                    new BitmapRegionTileSource.ResourceBitmapSource(
+                            mResources, mResId, BitmapRegionTileSource.MAX_PREVIEW_SIZE);
+            bitmapSource.loadInBackground();
+            BitmapRegionTileSource source = new BitmapRegionTileSource(a, bitmapSource);
             CropView v = a.getCropView();
             v.setTileSource(source, null);
             Point wallpaperSize = WallpaperCropActivity.getDefaultWallpaperSize(
@@ -200,6 +230,42 @@
         }
     }
 
+    public static class DefaultWallpaperInfo extends WallpaperTileInfo {
+        public Drawable mThumb;
+        public DefaultWallpaperInfo(Drawable thumb) {
+            mThumb = thumb;
+        }
+        @Override
+        public void onClick(WallpaperPickerActivity a) {
+            CropView c = a.getCropView();
+
+            Drawable defaultWallpaper = WallpaperManager.getInstance(a).getBuiltInDrawable(
+                    c.getWidth(), c.getHeight(), false, 0.5f, 0.5f);
+
+            c.setTileSource(
+                    new DrawableTileSource(a, defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE), null);
+            c.setScale(1f);
+            c.setTouchEnabled(false);
+        }
+        @Override
+        public void onSave(WallpaperPickerActivity a) {
+            try {
+                WallpaperManager.getInstance(a).clear();
+            } catch (IOException e) {
+                Log.w("Setting wallpaper to default threw exception", e);
+            }
+            a.finish();
+        }
+        @Override
+        public boolean isSelectable() {
+            return true;
+        }
+        @Override
+        public boolean isNamelessWallpaper() {
+            return true;
+        }
+    }
+
     public void setWallpaperStripYOffset(float offset) {
         mWallpaperStrip.setPadding(0, 0, 0, (int) offset);
     }
@@ -266,17 +332,8 @@
                     return;
                 }
                 WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
-                if (info.isSelectable()) {
-                    if (mSelectedThumb != null) {
-                        mSelectedThumb.setSelected(false);
-                        mSelectedThumb = null;
-                    }
-                    mSelectedThumb = v;
-                    v.setSelected(true);
-                    // TODO: Remove this once the accessibility framework and
-                    // services have better support for selection state.
-                    v.announceForAccessibility(
-                            getString(R.string.announce_selection, v.getContentDescription()));
+                if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
+                    selectTile(v);
                 }
                 info.onClick(WallpaperPickerActivity.this);
             }
@@ -305,12 +362,12 @@
         ArrayList<ResourceWallpaperInfo> wallpapers = findBundledWallpapers();
         mWallpapersView = (LinearLayout) findViewById(R.id.wallpaper_list);
         BuiltInWallpapersAdapter ia = new BuiltInWallpapersAdapter(this, wallpapers);
-        populateWallpapersFromAdapter(mWallpapersView, ia, false, true);
+        populateWallpapersFromAdapter(mWallpapersView, ia, false);
 
         // Populate the saved wallpapers
         mSavedImages = new SavedWallpaperImages(this);
         mSavedImages.loadThumbnailsAndImageIdList();
-        populateWallpapersFromAdapter(mWallpapersView, mSavedImages, true, true);
+        populateWallpapersFromAdapter(mWallpapersView, mSavedImages, true);
 
         // Populate the live wallpapers
         final LinearLayout liveWallpapersView =
@@ -319,7 +376,7 @@
         a.registerDataSetObserver(new DataSetObserver() {
             public void onChanged() {
                 liveWallpapersView.removeAllViews();
-                populateWallpapersFromAdapter(liveWallpapersView, a, false, false);
+                populateWallpapersFromAdapter(liveWallpapersView, a, false);
                 initializeScrollForRtl();
                 updateTileIndices();
             }
@@ -330,7 +387,7 @@
                 (LinearLayout) findViewById(R.id.third_party_wallpaper_list);
         final ThirdPartyWallpaperPickerListAdapter ta =
                 new ThirdPartyWallpaperPickerListAdapter(this);
-        populateWallpapersFromAdapter(thirdPartyWallpapersView, ta, false, false);
+        populateWallpapersFromAdapter(thirdPartyWallpapersView, ta, false);
 
         // Add a tile for the Gallery
         LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list);
@@ -354,7 +411,33 @@
         pickImageTile.setTag(pickImageInfo);
         pickImageInfo.setView(pickImageTile);
         pickImageTile.setOnClickListener(mThumbnailOnClickListener);
-        pickImageInfo.setView(pickImageTile);
+
+        // Add a tile for the default wallpaper
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            DefaultWallpaperInfo defaultWallpaperInfo = getDefaultWallpaper();
+            FrameLayout defaultWallpaperTile = (FrameLayout) createImageTileView(
+                    getLayoutInflater(), 0, null, mWallpapersView, defaultWallpaperInfo.mThumb);
+            setWallpaperItemPaddingToZero(defaultWallpaperTile);
+            defaultWallpaperTile.setTag(defaultWallpaperInfo);
+            mWallpapersView.addView(defaultWallpaperTile, 0);
+            defaultWallpaperTile.setOnClickListener(mThumbnailOnClickListener);
+            defaultWallpaperInfo.setView(defaultWallpaperTile);
+        }
+
+        // Select the first item; wait for a layout pass so that we initialize the dimensions of
+        // cropView or the defaultWallpaperView first
+        mCropView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                if ((right - left) > 0 && (bottom - top) > 0) {
+                    if (mWallpapersView.getChildCount() > 0) {
+                        mThumbnailOnClickListener.onClick(mWallpapersView.getChildAt(0));
+                    }
+                    v.removeOnLayoutChangeListener(this);
+                }
+            }
+        });
 
         updateTileIndices();
 
@@ -376,8 +459,8 @@
                 new View.OnClickListener() {
                     @Override
                     public void onClick(View v) {
-                        if (mSelectedThumb != null) {
-                            WallpaperTileInfo info = (WallpaperTileInfo) mSelectedThumb.getTag();
+                        if (mSelectedTile != null) {
+                            WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag();
                             info.onSave(WallpaperPickerActivity.this);
                         }
                     }
@@ -456,12 +539,25 @@
                     CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
                     c.setChecked(false);
                 }
-                mSelectedThumb.setSelected(true);
+                mSelectedTile.setSelected(true);
                 mActionMode = null;
             }
         };
     }
 
+    private void selectTile(View v) {
+        if (mSelectedTile != null) {
+            mSelectedTile.setSelected(false);
+            mSelectedTile = null;
+        }
+        mSelectedTile = v;
+        v.setSelected(true);
+        // TODO: Remove this once the accessibility framework and
+        // services have better support for selection state.
+        v.announceForAccessibility(
+                getString(R.string.announce_selection, v.getContentDescription()));
+    }
+
     private void initializeScrollForRtl() {
         final HorizontalScrollView scroll =
                 (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container);
@@ -520,7 +616,7 @@
     }
 
     private void populateWallpapersFromAdapter(ViewGroup parent, BaseAdapter adapter,
-            boolean addLongPressHandler, boolean selectFirstTile) {
+            boolean addLongPressHandler) {
         for (int i = 0; i < adapter.getCount(); i++) {
             FrameLayout thumbnail = (FrameLayout) adapter.getView(i, null, parent);
             parent.addView(thumbnail, i);
@@ -531,9 +627,6 @@
                 addLongPressHandler(thumbnail);
             }
             thumbnail.setOnClickListener(mThumbnailOnClickListener);
-            if (i == 0 && selectFirstTile) {
-                mThumbnailOnClickListener.onClick(thumbnail);
-            }
         }
     }
 
@@ -623,26 +716,35 @@
         }
     }
 
-    private void addTemporaryWallpaperTile(Uri uri) {
+    private void addTemporaryWallpaperTile(final Uri uri) {
         mTempWallpaperTiles.add(uri);
         // Add a tile for the image picked from Gallery
-        FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
+        final FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
                 inflate(R.layout.wallpaper_picker_item, mWallpapersView, false);
+        pickedImageThumbnail.setVisibility(View.GONE);
         setWallpaperItemPaddingToZero(pickedImageThumbnail);
+        mWallpapersView.addView(pickedImageThumbnail, 0);
 
         // Load the thumbnail
-        ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
-        Point defaultSize = getDefaultThumbnailSize(this.getResources());
-        int rotation = WallpaperCropActivity.getRotationFromExif(this, uri);
-        Bitmap thumb = createThumbnail(defaultSize, this, uri, null, null, 0, rotation, false);
-        if (thumb != null) {
-            image.setImageBitmap(thumb);
-            Drawable thumbDrawable = image.getDrawable();
-            thumbDrawable.setDither(true);
-        } else {
-            Log.e(TAG, "Error loading thumbnail for uri=" + uri);
-        }
-        mWallpapersView.addView(pickedImageThumbnail, 0);
+        final ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
+        final Point defaultSize = getDefaultThumbnailSize(this.getResources());
+        final Context context = this;
+        new AsyncTask<Void, Bitmap, Bitmap>() {
+            protected Bitmap doInBackground(Void...args) {
+                int rotation = WallpaperCropActivity.getRotationFromExif(context, uri);
+                return createThumbnail(defaultSize, context, uri, null, null, 0, rotation, false);
+
+            }
+            protected void onPostExecute(Bitmap thumb) {
+                if (thumb != null) {
+                    image.setImageBitmap(thumb);
+                    Drawable thumbDrawable = image.getDrawable();
+                    thumbDrawable.setDither(true);
+                } else {
+                    Log.e(TAG, "Error loading thumbnail for uri=" + uri);
+                }
+            }
+        }.execute();
 
         UriWallpaperInfo info = new UriWallpaperInfo(uri);
         pickedImageThumbnail.setTag(info);
@@ -700,18 +802,35 @@
         }
 
         // Add an entry for the default wallpaper (stored in system resources)
-        ResourceWallpaperInfo defaultWallpaperInfo = getDefaultWallpaperInfo();
-        if (defaultWallpaperInfo != null) {
-            bundledWallpapers.add(0, defaultWallpaperInfo);
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+            ResourceWallpaperInfo defaultWallpaperInfo = getPreKKDefaultWallpaperInfo();
+            if (defaultWallpaperInfo != null) {
+                bundledWallpapers.add(0, defaultWallpaperInfo);
+            }
         }
         return bundledWallpapers;
     }
 
-    private ResourceWallpaperInfo getDefaultWallpaperInfo() {
+    private boolean writeImageToFileAsJpeg(File f, Bitmap b) {
+        try {
+            f.createNewFile();
+            FileOutputStream thumbFileStream =
+                    openFileOutput(f.getName(), Context.MODE_PRIVATE);
+            b.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream);
+            thumbFileStream.close();
+            return true;
+        } catch (IOException e) {
+            Log.e(TAG, "Error while writing bitmap to file " + e);
+            f.delete();
+        }
+        return false;
+    }
+
+    private ResourceWallpaperInfo getPreKKDefaultWallpaperInfo() {
         Resources sysRes = Resources.getSystem();
         int resId = sysRes.getIdentifier("default_wallpaper", "drawable", "android");
 
-        File defaultThumbFile = new File(getFilesDir(), "default_thumb.jpg");
+        File defaultThumbFile = new File(getFilesDir(), DEFAULT_WALLPAPER_THUMBNAIL_FILENAME);
         Bitmap thumb = null;
         boolean defaultWallpaperExists = false;
         if (defaultThumbFile.exists()) {
@@ -724,17 +843,7 @@
             thumb = createThumbnail(
                     defaultThumbSize, this, null, null, sysRes, resId, rotation, false);
             if (thumb != null) {
-                try {
-                    defaultThumbFile.createNewFile();
-                    FileOutputStream thumbFileStream =
-                            openFileOutput(defaultThumbFile.getName(), Context.MODE_PRIVATE);
-                    thumb.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream);
-                    thumbFileStream.close();
-                    defaultWallpaperExists = true;
-                } catch (IOException e) {
-                    Log.e(TAG, "Error while writing default wallpaper thumbnail to file " + e);
-                    defaultThumbFile.delete();
-                }
+                defaultWallpaperExists = writeImageToFileAsJpeg(defaultThumbFile, thumb);
             }
         }
         if (defaultWallpaperExists) {
@@ -743,6 +852,37 @@
         return null;
     }
 
+    private DefaultWallpaperInfo getDefaultWallpaper() {
+        File defaultThumbFile = new File(getFilesDir(), DEFAULT_WALLPAPER_THUMBNAIL_FILENAME);
+        Bitmap thumb = null;
+        boolean defaultWallpaperExists = false;
+        if (defaultThumbFile.exists()) {
+            thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath());
+            defaultWallpaperExists = true;
+        } else {
+            Resources res = getResources();
+            Point defaultThumbSize = getDefaultThumbnailSize(res);
+            Paint p = new Paint();
+            p.setFilterBitmap(true);
+            Drawable wallpaperDrawable = WallpaperManager.getInstance(this).getBuiltInDrawable(
+                    defaultThumbSize.x, defaultThumbSize.y, true, 0.5f, 0.5f);
+            if (wallpaperDrawable != null) {
+                thumb = Bitmap.createBitmap(
+                        defaultThumbSize.x, defaultThumbSize.y, Bitmap.Config.ARGB_8888);
+                Canvas c = new Canvas(thumb);
+                wallpaperDrawable.draw(c);
+                c.setBitmap(null);
+            }
+            if (thumb != null) {
+                defaultWallpaperExists = writeImageToFileAsJpeg(defaultThumbFile, thumb);
+            }
+        }
+        if (defaultWallpaperExists) {
+            return new DefaultWallpaperInfo(new BitmapDrawable(thumb));
+        }
+        return null;
+    }
+
     public Pair<ApplicationInfo, Integer> getWallpaperArrayResourceId() {
         // Context.getPackageName() may return the "original" package name,
         // com.android.launcher3; Resources needs the real package name,
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index f6416c8..8c2c89d 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -22,6 +22,7 @@
 import android.animation.AnimatorSet;
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -89,6 +90,9 @@
     private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
     private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
 
+    protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
+    protected static final int FADE_EMPTY_SCREEN_DURATION = 150;
+
     private static final int BACKGROUND_FADE_OUT_DURATION = 350;
     private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
     private static final int FLING_THRESHOLD_VELOCITY = 500;
@@ -128,6 +132,8 @@
     private HashMap<Long, CellLayout> mWorkspaceScreens = new HashMap<Long, CellLayout>();
     private ArrayList<Long> mScreenOrder = new ArrayList<Long>();
 
+    private Runnable mRemoveEmptyScreenRunnable;
+
     /**
      * CellInfo for the cell that is currently being dragged
      */
@@ -304,7 +310,8 @@
 
         mLauncher = (Launcher) context;
         final Resources res = getResources();
-        mWorkspaceFadeInAdjacentScreens = res.getBoolean(R.bool.config_workspaceFadeAdjacentScreens);
+        mWorkspaceFadeInAdjacentScreens = LauncherAppState.getInstance().getDynamicGrid().
+                getDeviceProfile().shouldFadeAdjacentWorkspaceScreens();
         mFadeInAdjacentScreens = false;
         mWallpaperManager = WallpaperManager.getInstance(context);
 
@@ -392,7 +399,6 @@
         InstallShortcutReceiver.disableAndFlushInstallQueue(getContext());
         UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext());
 
-        removeExtraEmptyScreen();
         mDragSourceInternal = null;
         mLauncher.onInteractionEnd();
     }
@@ -627,6 +633,9 @@
         boolean lastChildOnScreen = false;
         boolean childOnFinalScreen = false;
 
+        // Cancel any pending removal of empty screen
+        mRemoveEmptyScreenRunnable = null;
+
         if (mDragSourceInternal != null) {
             if (mDragSourceInternal.getChildCount() == 1) {
                 lastChildOnScreen = true;
@@ -654,15 +663,98 @@
         return false;
     }
 
-    public void removeExtraEmptyScreen() {
-        if (hasExtraEmptyScreen()) {
-            CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
-            mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
-            mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
-            removeView(cl);
+    private void convertFinalScreenToEmptyScreenIfNecessary() {
+        if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
+        long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
+
+        if (finalScreenId == CUSTOM_CONTENT_SCREEN_ID) return;
+        CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId);
+
+        // If the final screen is empty, convert it to the extra empty screen
+        if (finalScreen.getShortcutsAndWidgets().getChildCount() == 0 &&
+                !finalScreen.isDropPending()) {
+            mWorkspaceScreens.remove(finalScreenId);
+            mScreenOrder.remove(finalScreenId);
+
+            // if this is the last non-custom content screen, convert it to the empty screen
+            mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
+            mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
         }
     }
 
+    public void removeExtraEmptyScreen(final boolean animate, final Runnable onComplete) {
+        removeExtraEmptyScreen(animate, onComplete, 0, false);
+    }
+
+    public void removeExtraEmptyScreen(final boolean animate, final Runnable onComplete,
+            final int delay, final boolean stripEmptyScreens) {
+        if (delay > 0) {
+            postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    removeExtraEmptyScreen(animate, onComplete, 0, stripEmptyScreens);
+                }
+
+            }, delay);
+            return;
+        }
+
+        convertFinalScreenToEmptyScreenIfNecessary();
+        if (hasExtraEmptyScreen()) {
+            int emptyIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
+            if (getNextPage() == emptyIndex) {
+                snapToPage(getNextPage() - 1, SNAP_OFF_EMPTY_SCREEN_DURATION);
+                fadeAndRemoveEmptyScreen(SNAP_OFF_EMPTY_SCREEN_DURATION, FADE_EMPTY_SCREEN_DURATION,
+                        onComplete, stripEmptyScreens);
+            } else {
+                fadeAndRemoveEmptyScreen(0, FADE_EMPTY_SCREEN_DURATION,
+                        onComplete, stripEmptyScreens);
+            }
+            return;
+        }
+        if (onComplete != null) {
+            onComplete.run();
+        }
+    }
+
+    private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete,
+            final boolean stripEmptyScreens) {
+        PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f);
+        PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 0f);
+
+        final CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
+
+        mRemoveEmptyScreenRunnable = new Runnable() {
+            @Override
+            public void run() {
+                if (hasExtraEmptyScreen()) {
+                    mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
+                    mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
+                    removeView(cl);
+                    if (stripEmptyScreens) {
+                        stripEmptyScreens();
+                    }
+                }
+            }
+        };
+
+        ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(cl, alpha, bgAlpha);
+        oa.setDuration(duration);
+        oa.setStartDelay(delay);
+        oa.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mRemoveEmptyScreenRunnable != null) {
+                    mRemoveEmptyScreenRunnable.run();
+                }
+                if (onComplete != null) {
+                    onComplete.run();
+                }
+            }
+        });
+        oa.start();
+    }
+
     public boolean hasExtraEmptyScreen() {
         int nScreens = getChildCount();
         nScreens = nScreens - numCustomPages();
@@ -753,6 +845,7 @@
                 removeView(cl);
             } else {
                 // if this is the last non-custom content screen, convert it to the empty screen
+                mRemoveEmptyScreenRunnable = null;
                 mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl);
                 mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
             }
@@ -1097,17 +1190,21 @@
 
     protected void setWallpaperDimension() {
         String spKey = WallpaperCropActivity.getSharedPreferencesKey();
-        SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_PRIVATE);
+        SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
         WallpaperPickerActivity.suggestWallpaperDimension(mLauncher.getResources(),
                 sp, mLauncher.getWindowManager(), mWallpaperManager);
     }
 
     protected void snapToPage(int whichPage, Runnable r) {
+        snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION, r);
+    }
+
+    protected void snapToPage(int whichPage, int duration, Runnable r) {
         if (mDelayedSnapToPageRunnable != null) {
             mDelayedSnapToPageRunnable.run();
         }
         mDelayedSnapToPageRunnable = r;
-        snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION);
+        snapToPage(whichPage, duration);
     }
 
     protected void snapToScreenId(long screenId, Runnable r) {
@@ -1199,10 +1296,8 @@
                 // TODO: do different behavior if it's  a live wallpaper?
                 // Sometimes the left parameter of the pages is animated during a layout transition;
                 // this parameter offsets it to keep the wallpaper from animating as well
-                int offsetForLayoutTransitionAnimation = isLayoutRtl() ?
-                        getPageAt(getChildCount() - 1).getLeft() - getFirstChildLeft() : 0;
                 int adjustedScroll =
-                        getScrollX() - firstPageScrollX - offsetForLayoutTransitionAnimation;
+                        getScrollX() - firstPageScrollX - getLayoutTransitionOffsetForPage(0);
                 float offset = Math.min(1, adjustedScroll / (float) scrollRange);
                 offset = Math.max(0, offset);
                 // Don't use up all the wallpaper parallax until you have at least
@@ -1395,12 +1490,6 @@
                     float scrollProgress = getScrollProgress(screenCenter, child, i);
                     float alpha = 1 - Math.abs(scrollProgress);
                     child.getShortcutsAndWidgets().setAlpha(alpha);
-                    if (!mIsDragOccuring) {
-                        child.setBackgroundAlphaMultiplier(
-                                backgroundAlphaInterpolator(Math.abs(scrollProgress)));
-                    } else {
-                        child.setBackgroundAlphaMultiplier(1f);
-                    }
                 }
             }
         }
@@ -2015,7 +2104,14 @@
             final CellLayout cl = (CellLayout) getChildAt(i);
             boolean isCurrentPage = (i == getNextPage());
             float initialAlpha = cl.getShortcutsAndWidgets().getAlpha();
-            float finalAlpha = stateIsSmall ? 0f : 1f;
+            float finalAlpha;
+            if (stateIsSmall) {
+                finalAlpha = 0f;
+            } else if (stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
+                finalAlpha = i == getNextPage() ? 1f : 0f;
+            } else {
+                finalAlpha = 1f;
+            }
 
             // If we are animating to/from the small state, then hide the side pages and fade the
             // current page in
@@ -2728,13 +2824,13 @@
                 // cell also contains a shortcut, then create a folder with the two shortcuts.
                 if (!mInScrollArea && createUserFolderIfNecessary(cell, container,
                         dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) {
-                    stripEmptyScreens();
+                    removeExtraEmptyScreen(true, null, 0, true);
                     return;
                 }
 
                 if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
                         distance, d, false)) {
-                    stripEmptyScreens();
+                    removeExtraEmptyScreen(true, null, 0, true);
                     return;
                 }
 
@@ -2842,7 +2938,7 @@
                     if (finalResizeRunnable != null) {
                         finalResizeRunnable.run();
                     }
-                    stripEmptyScreens();
+                    removeExtraEmptyScreen(true, null, 0, true);
                 }
             };
             mAnimatingViewIntoPlace = true;
@@ -3493,7 +3589,13 @@
         final Runnable exitSpringLoadedRunnable = new Runnable() {
             @Override
             public void run() {
-                mLauncher.exitSpringLoadedDragModeDelayed(true, false, null);
+                removeExtraEmptyScreen(false, new Runnable() {
+                    @Override
+                    public void run() {
+                        mLauncher.exitSpringLoadedDragModeDelayed(true,
+                                Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+                    }
+                });
             }
         };
 
@@ -3654,7 +3756,7 @@
                 // the correct final location.
                 setFinalTransitionTransform(cellLayout);
                 mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view,
-                        exitSpringLoadedRunnable);
+                        exitSpringLoadedRunnable, this);
                 resetTransitionTransform(cellLayout);
             }
         }
@@ -3729,7 +3831,7 @@
                 external, scalePreview);
 
         Resources res = mLauncher.getResources();
-        int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
+        final int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
 
         // In the case where we've prebound the widget, we remove it from the DragLayer
         if (finalView instanceof AppWidgetHostView && external) {
@@ -3837,11 +3939,11 @@
             final boolean isFlingToDelete, final boolean success) {
         if (mDeferDropAfterUninstall) {
             mDeferredAction = new Runnable() {
-                    public void run() {
-                        onDropCompleted(target, d, isFlingToDelete, success);
-                        mDeferredAction = null;
-                    }
-                };
+                public void run() {
+                    onDropCompleted(target, d, isFlingToDelete, success);
+                    mDeferredAction = null;
+                }
+            };
             return;
         }
 
@@ -3859,7 +3961,7 @@
                 // If we move the item to anything not on the Workspace, check if any empty
                 // screens need to be removed. If we dropped back on the workspace, this will
                 // be done post drop animation.
-                stripEmptyScreens();
+                removeExtraEmptyScreen(true, null, 0, true);
             }
         } else if (mDragInfo != null) {
             CellLayout cellLayout;
@@ -4384,29 +4486,45 @@
         stripEmptyScreens();
     }
 
+    private void updateShortcut(HashMap<ComponentName, AppInfo> appsMap, ItemInfo info,
+                                View child) {
+        ComponentName cn = info.getIntent().getComponent();
+        if (cn != null) {
+            AppInfo appInfo = appsMap.get(info.getIntent().getComponent());
+            if ((appInfo != null) && LauncherModel.isShortcutInfoUpdateable(info)) {
+                ShortcutInfo shortcutInfo = (ShortcutInfo) info;
+                BubbleTextView shortcut = (BubbleTextView) child;
+                shortcutInfo.updateIcon(mIconCache);
+                shortcutInfo.title = appInfo.title.toString();
+                shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache);
+            }
+        }
+    }
+
     void updateShortcuts(ArrayList<AppInfo> apps) {
+        // Create a map of the apps to test against
+        final HashMap<ComponentName, AppInfo> appsMap = new HashMap<ComponentName, AppInfo>();
+        for (AppInfo ai : apps) {
+            appsMap.put(ai.componentName, ai);
+        }
+
         ArrayList<ShortcutAndWidgetContainer> childrenLayouts = getAllShortcutAndWidgetContainers();
         for (ShortcutAndWidgetContainer layout: childrenLayouts) {
-            int childCount = layout.getChildCount();
-            for (int j = 0; j < childCount; j++) {
-                final View view = layout.getChildAt(j);
-                Object tag = view.getTag();
-
-                if (LauncherModel.isShortcutInfoUpdateable((ItemInfo) tag)) {
-                    ShortcutInfo info = (ShortcutInfo) tag;
-
-                    final Intent intent = info.intent;
-                    final ComponentName name = intent.getComponent();
-                    final int appCount = apps.size();
-                    for (int k = 0; k < appCount; k++) {
-                        AppInfo app = apps.get(k);
-                        if (app.componentName.equals(name)) {
-                            BubbleTextView shortcut = (BubbleTextView) view;
-                            info.updateIcon(mIconCache);
-                            info.title = app.title.toString();
-                            shortcut.applyFromShortcutInfo(info, mIconCache);
-                        }
+            // Update all the children shortcuts
+            final HashMap<ItemInfo, View> children = new HashMap<ItemInfo, View>();
+            for (int j = 0; j < layout.getChildCount(); j++) {
+                View v = layout.getChildAt(j);
+                ItemInfo info = (ItemInfo) v.getTag();
+                if (info instanceof FolderInfo && v instanceof FolderIcon) {
+                    FolderIcon folder = (FolderIcon) v;
+                    ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
+                    for (View fv : folderChildren) {
+                        info = (ItemInfo) fv.getTag();
+                        updateShortcut(appsMap, info, fv);
                     }
+                    folder.invalidate();
+                } else if (info instanceof ShortcutInfo) {
+                    updateShortcut(appsMap, info, v);
                 }
             }
         }
diff --git a/src/com/android/photos/BitmapRegionTileSource.java b/src/com/android/photos/BitmapRegionTileSource.java
index 5f64018..2f203af 100644
--- a/src/com/android/photos/BitmapRegionTileSource.java
+++ b/src/com/android/photos/BitmapRegionTileSource.java
@@ -24,6 +24,9 @@
 import android.graphics.BitmapFactory;
 import android.graphics.BitmapRegionDecoder;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Build;
@@ -31,14 +34,114 @@
 import android.util.Log;
 
 import com.android.gallery3d.common.BitmapUtils;
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.exif.ExifInterface;
 import com.android.gallery3d.glrenderer.BasicTexture;
 import com.android.gallery3d.glrenderer.BitmapTexture;
 import com.android.photos.views.TiledImageRenderer;
 
 import java.io.BufferedInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 
+interface SimpleBitmapRegionDecoder {
+    int getWidth();
+    int getHeight();
+    Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options);
+}
+
+class SimpleBitmapRegionDecoderWrapper implements SimpleBitmapRegionDecoder {
+    BitmapRegionDecoder mDecoder;
+    private SimpleBitmapRegionDecoderWrapper(BitmapRegionDecoder decoder) {
+        mDecoder = decoder;
+    }
+    public static SimpleBitmapRegionDecoderWrapper newInstance(
+            String pathName, boolean isShareable) {
+        try {
+            BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(pathName, isShareable);
+            if (d != null) {
+                return new SimpleBitmapRegionDecoderWrapper(d);
+            }
+        } catch (IOException e) {
+            Log.w("BitmapRegionTileSource", "getting decoder failed for path " + pathName, e);
+            return null;
+        }
+        return null;
+    }
+    public static SimpleBitmapRegionDecoderWrapper newInstance(
+            InputStream is, boolean isShareable) {
+        try {
+            BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(is, isShareable);
+            if (d != null) {
+                return new SimpleBitmapRegionDecoderWrapper(d);
+            }
+        } catch (IOException e) {
+            Log.w("BitmapRegionTileSource", "getting decoder failed", e);
+            return null;
+        }
+        return null;
+    }
+    public int getWidth() {
+        return mDecoder.getWidth();
+    }
+    public int getHeight() {
+        return mDecoder.getHeight();
+    }
+    public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) {
+        return mDecoder.decodeRegion(wantRegion, options);
+    }
+}
+
+class DumbBitmapRegionDecoder implements SimpleBitmapRegionDecoder {
+    //byte[] mStreamCopy;
+    Bitmap mBuffer;
+    Canvas mTempCanvas;
+    Paint mTempPaint;
+    private DumbBitmapRegionDecoder(Bitmap b) {
+        mBuffer = b;
+    }
+    public static DumbBitmapRegionDecoder newInstance(String pathName) {
+        Bitmap b = BitmapFactory.decodeFile(pathName);
+        if (b != null) {
+            return new DumbBitmapRegionDecoder(b);
+        }
+        return null;
+    }
+    public static DumbBitmapRegionDecoder newInstance(InputStream is) {
+        Bitmap b = BitmapFactory.decodeStream(is);
+        if (b != null) {
+            return new DumbBitmapRegionDecoder(b);
+        }
+        return null;
+    }
+    public int getWidth() {
+        return mBuffer.getWidth();
+    }
+    public int getHeight() {
+        return mBuffer.getHeight();
+    }
+    public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) {
+        if (mTempCanvas == null) {
+            mTempCanvas = new Canvas();
+            mTempPaint = new Paint();
+            mTempPaint.setFilterBitmap(true);
+        }
+        int sampleSize = Math.max(options.inSampleSize, 1);
+        Bitmap newBitmap = Bitmap.createBitmap(
+                wantRegion.width() / sampleSize,
+                wantRegion.height() / sampleSize,
+                Bitmap.Config.ARGB_8888);
+        mTempCanvas.setBitmap(newBitmap);
+        mTempCanvas.save();
+        mTempCanvas.scale(1f / sampleSize, 1f / sampleSize);
+        mTempCanvas.drawBitmap(mBuffer, -wantRegion.left, -wantRegion.top, mTempPaint);
+        mTempCanvas.restore();
+        mTempCanvas.setBitmap(null);
+        return newBitmap;
+    }
+}
+
 /**
  * A {@link com.android.photos.views.TiledImageRenderer.TileSource} using
  * {@link BitmapRegionDecoder} to wrap a local file
@@ -53,9 +156,214 @@
     private static final int GL_SIZE_LIMIT = 2048;
     // This must be no larger than half the size of the GL_SIZE_LIMIT
     // due to decodePreview being allowed to be up to 2x the size of the target
-    private static final int MAX_PREVIEW_SIZE = 1024;
+    public static final int MAX_PREVIEW_SIZE = GL_SIZE_LIMIT / 2;
 
-    BitmapRegionDecoder mDecoder;
+    public static abstract class BitmapSource {
+        private SimpleBitmapRegionDecoder mDecoder;
+        private Bitmap mPreview;
+        private int mPreviewSize;
+        private int mRotation;
+        public enum State { NOT_LOADED, LOADED, ERROR_LOADING };
+        private State mState = State.NOT_LOADED;
+        public BitmapSource(int previewSize) {
+            mPreviewSize = previewSize;
+        }
+        public boolean loadInBackground() {
+            ExifInterface ei = new ExifInterface();
+            if (readExif(ei)) {
+                Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
+                if (ori != null) {
+                    mRotation = ExifInterface.getRotationForOrientationValue(ori.shortValue());
+                }
+            }
+            mDecoder = loadBitmapRegionDecoder();
+            if (mDecoder == null) {
+                mState = State.ERROR_LOADING;
+                return false;
+            } else {
+                int width = mDecoder.getWidth();
+                int height = mDecoder.getHeight();
+                if (mPreviewSize != 0) {
+                    int previewSize = Math.min(mPreviewSize, MAX_PREVIEW_SIZE);
+                    BitmapFactory.Options opts = new BitmapFactory.Options();
+                    opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
+                    opts.inPreferQualityOverSpeed = true;
+
+                    float scale = (float) previewSize / Math.max(width, height);
+                    opts.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
+                    opts.inJustDecodeBounds = false;
+                    mPreview = loadPreviewBitmap(opts);
+                }
+                mState = State.LOADED;
+                return true;
+            }
+        }
+
+        public State getLoadingState() {
+            return mState;
+        }
+
+        public SimpleBitmapRegionDecoder getBitmapRegionDecoder() {
+            return mDecoder;
+        }
+
+        public Bitmap getPreviewBitmap() {
+            return mPreview;
+        }
+
+        public int getPreviewSize() {
+            return mPreviewSize;
+        }
+
+        public int getRotation() {
+            return mRotation;
+        }
+
+        public abstract boolean readExif(ExifInterface ei);
+        public abstract SimpleBitmapRegionDecoder loadBitmapRegionDecoder();
+        public abstract Bitmap loadPreviewBitmap(BitmapFactory.Options options);
+    }
+
+    public static class FilePathBitmapSource extends BitmapSource {
+        private String mPath;
+        public FilePathBitmapSource(String path, int previewSize) {
+            super(previewSize);
+            mPath = path;
+        }
+        @Override
+        public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() {
+            SimpleBitmapRegionDecoder d;
+            d = SimpleBitmapRegionDecoderWrapper.newInstance(mPath, true);
+            if (d == null) {
+                d = DumbBitmapRegionDecoder.newInstance(mPath);
+            }
+            return d;
+        }
+        @Override
+        public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
+            return BitmapFactory.decodeFile(mPath, options);
+        }
+        @Override
+        public boolean readExif(ExifInterface ei) {
+            try {
+                ei.readExif(mPath);
+                return true;
+            } catch (IOException e) {
+                Log.w("BitmapRegionTileSource", "getting decoder failed", e);
+                return false;
+            }
+        }
+    }
+
+    public static class UriBitmapSource extends BitmapSource {
+        private Context mContext;
+        private Uri mUri;
+        public UriBitmapSource(Context context, Uri uri, int previewSize) {
+            super(previewSize);
+            mContext = context;
+            mUri = uri;
+        }
+        private InputStream regenerateInputStream() throws FileNotFoundException {
+            InputStream is = mContext.getContentResolver().openInputStream(mUri);
+            return new BufferedInputStream(is);
+        }
+        @Override
+        public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() {
+            try {
+                InputStream is = regenerateInputStream();
+                SimpleBitmapRegionDecoder regionDecoder =
+                        SimpleBitmapRegionDecoderWrapper.newInstance(is, false);
+                Utils.closeSilently(is);
+                if (regionDecoder == null) {
+                    is = regenerateInputStream();
+                    regionDecoder = DumbBitmapRegionDecoder.newInstance(is);
+                    Utils.closeSilently(is);
+                }
+                return regionDecoder;
+            } catch (FileNotFoundException e) {
+                Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
+                return null;
+            } catch (IOException e) {
+                Log.e("BitmapRegionTileSource", "Failure while reading URI " + mUri, e);
+                return null;
+            }
+        }
+        @Override
+        public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
+            try {
+                InputStream is = regenerateInputStream();
+                Bitmap b = BitmapFactory.decodeStream(is, null, options);
+                Utils.closeSilently(is);
+                return b;
+            } catch (FileNotFoundException e) {
+                Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
+                return null;
+            }
+        }
+        @Override
+        public boolean readExif(ExifInterface ei) {
+            InputStream is = null;
+            try {
+                is = regenerateInputStream();
+                ei.readExif(is);
+                Utils.closeSilently(is);
+                return true;
+            } catch (FileNotFoundException e) {
+                Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
+                return false;
+            } catch (IOException e) {
+                Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
+                return false;
+            } finally {
+                Utils.closeSilently(is);
+            }
+        }
+    }
+
+    public static class ResourceBitmapSource extends BitmapSource {
+        private Resources mRes;
+        private int mResId;
+        public ResourceBitmapSource(Resources res, int resId, int previewSize) {
+            super(previewSize);
+            mRes = res;
+            mResId = resId;
+        }
+        private InputStream regenerateInputStream() {
+            InputStream is = mRes.openRawResource(mResId);
+            return new BufferedInputStream(is);
+        }
+        @Override
+        public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() {
+            InputStream is = regenerateInputStream();
+            SimpleBitmapRegionDecoder regionDecoder =
+                    SimpleBitmapRegionDecoderWrapper.newInstance(is, false);
+            Utils.closeSilently(is);
+            if (regionDecoder == null) {
+                is = regenerateInputStream();
+                regionDecoder = DumbBitmapRegionDecoder.newInstance(is);
+                Utils.closeSilently(is);
+            }
+            return regionDecoder;
+        }
+        @Override
+        public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
+            return BitmapFactory.decodeResource(mRes, mResId, options);
+        }
+        @Override
+        public boolean readExif(ExifInterface ei) {
+            try {
+                InputStream is = regenerateInputStream();
+                ei.readExif(is);
+                Utils.closeSilently(is);
+                return true;
+            } catch (IOException e) {
+                Log.e("BitmapRegionTileSource", "Error reading resource", e);
+                return false;
+            }
+        }
+    }
+
+    SimpleBitmapRegionDecoder mDecoder;
     int mWidth;
     int mHeight;
     int mTileSize;
@@ -68,58 +376,33 @@
     private BitmapFactory.Options mOptions;
     private Canvas mCanvas;
 
-    public BitmapRegionTileSource(Context context, String path, int previewSize, int rotation) {
-        this(null, context, path, null, 0, previewSize, rotation);
-    }
-
-    public BitmapRegionTileSource(Context context, Uri uri, int previewSize, int rotation) {
-        this(null, context, null, uri, 0, previewSize, rotation);
-    }
-
-    public BitmapRegionTileSource(Resources res,
-            Context context, int resId, int previewSize, int rotation) {
-        this(res, context, null, null, resId, previewSize, rotation);
-    }
-
-    private BitmapRegionTileSource(Resources res,
-            Context context, String path, Uri uri, int resId, int previewSize, int rotation) {
+    public BitmapRegionTileSource(Context context, BitmapSource source) {
         mTileSize = TiledImageRenderer.suggestedTileSize(context);
-        mRotation = rotation;
-        try {
-            if (path != null) {
-                mDecoder = BitmapRegionDecoder.newInstance(path, true);
-            } else if (uri != null) {
-                InputStream is = context.getContentResolver().openInputStream(uri);
-                BufferedInputStream bis = new BufferedInputStream(is);
-                mDecoder = BitmapRegionDecoder.newInstance(bis, true);
-            } else {
-                InputStream is = res.openRawResource(resId);
-                BufferedInputStream bis = new BufferedInputStream(is);
-                mDecoder = BitmapRegionDecoder.newInstance(bis, true);
-            }
+        mRotation = source.getRotation();
+        mDecoder = source.getBitmapRegionDecoder();
+        if (mDecoder != null) {
             mWidth = mDecoder.getWidth();
             mHeight = mDecoder.getHeight();
-        } catch (IOException e) {
-            Log.w("BitmapRegionTileSource", "ctor failed", e);
-        }
-        mOptions = new BitmapFactory.Options();
-        mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
-        mOptions.inPreferQualityOverSpeed = true;
-        mOptions.inTempStorage = new byte[16 * 1024];
-        if (previewSize != 0) {
-            previewSize = Math.min(previewSize, MAX_PREVIEW_SIZE);
-            // Although this is the same size as the Bitmap that is likely already
-            // loaded, the lifecycle is different and interactions are on a different
-            // thread. Thus to simplify, this source will decode its own bitmap.
-            Bitmap preview = decodePreview(res, context, path, uri, resId, previewSize);
-            if (preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) {
-                mPreview = new BitmapTexture(preview);
-            } else {
-                Log.w(TAG, String.format(
-                        "Failed to create preview of apropriate size! "
-                        + " in: %dx%d, out: %dx%d",
-                        mWidth, mHeight,
-                        preview.getWidth(), preview.getHeight()));
+            mOptions = new BitmapFactory.Options();
+            mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
+            mOptions.inPreferQualityOverSpeed = true;
+            mOptions.inTempStorage = new byte[16 * 1024];
+            int previewSize = source.getPreviewSize();
+            if (previewSize != 0) {
+                previewSize = Math.min(previewSize, MAX_PREVIEW_SIZE);
+                // Although this is the same size as the Bitmap that is likely already
+                // loaded, the lifecycle is different and interactions are on a different
+                // thread. Thus to simplify, this source will decode its own bitmap.
+                Bitmap preview = decodePreview(source, previewSize);
+                if (preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) {
+                    mPreview = new BitmapTexture(preview);
+                } else {
+                    Log.w(TAG, String.format(
+                            "Failed to create preview of apropriate size! "
+                            + " in: %dx%d, out: %dx%d",
+                            mWidth, mHeight,
+                            preview.getWidth(), preview.getHeight()));
+                }
             }
         }
     }
@@ -215,33 +498,15 @@
      * Note that the returned bitmap may have a long edge that's longer
      * than the targetSize, but it will always be less than 2x the targetSize
      */
-    private Bitmap decodePreview(
-            Resources res, Context context, String file, Uri uri, int resId, int targetSize) {
-        float scale = (float) targetSize / Math.max(mWidth, mHeight);
-        mOptions.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
-        mOptions.inJustDecodeBounds = false;
-
-        Bitmap result = null;
-        if (file != null) {
-            result = BitmapFactory.decodeFile(file, mOptions);
-        } else if (uri != null) {
-            try {
-                InputStream is = context.getContentResolver().openInputStream(uri);
-                BufferedInputStream bis = new BufferedInputStream(is);
-                result = BitmapFactory.decodeStream(bis, null, mOptions);
-            } catch (IOException e) {
-                Log.w("BitmapRegionTileSource", "getting preview failed", e);
-            }
-        } else {
-            result = BitmapFactory.decodeResource(res, resId, mOptions);
-        }
+    private Bitmap decodePreview(BitmapSource source, int targetSize) {
+        Bitmap result = source.getPreviewBitmap();
         if (result == null) {
             return null;
         }
 
         // We need to resize down if the decoder does not support inSampleSize
         // or didn't support the specified inSampleSize (some decoders only do powers of 2)
-        scale = (float) targetSize / (float) (Math.max(result.getWidth(), result.getHeight()));
+        float scale = (float) targetSize / (float) (Math.max(result.getWidth(), result.getHeight()));
 
         if (scale <= 0.5) {
             result = BitmapUtils.resizeBitmapByScale(result, scale, true);
diff --git a/update_system_wallpaper_cropper.py b/update_system_wallpaper_cropper.py
new file mode 100644
index 0000000..5d24f6f
--- /dev/null
+++ b/update_system_wallpaper_cropper.py
@@ -0,0 +1,55 @@
+# This script is used to push the most up-to-date files from
+# Launcher into frameworks' version of the WallpaperCropActivity
+# (and supporting files)
+# The framework versions have some small modifications that are
+# necessary so do this with care
+import os
+import sys
+files = """
+src/android/util/Pools.java
+src/com/android/gallery3d/util/IntArray.java
+src/com/android/gallery3d/common/Utils.java
+src/com/android/gallery3d/exif/ByteBufferInputStream.java
+src/com/android/gallery3d/exif/CountedDataInputStream.java
+src/com/android/gallery3d/exif/ExifData.java
+src/com/android/gallery3d/exif/ExifInterface.java
+src/com/android/gallery3d/exif/ExifInvalidFormatException.java
+src/com/android/gallery3d/exif/ExifModifier.java
+src/com/android/gallery3d/exif/ExifOutputStream.java
+src/com/android/gallery3d/exif/ExifParser.java
+src/com/android/gallery3d/exif/ExifReader.java
+src/com/android/gallery3d/exif/ExifTag.java
+src/com/android/gallery3d/exif/IfdData.java
+src/com/android/gallery3d/exif/IfdId.java
+src/com/android/gallery3d/exif/JpegHeader.java
+src/com/android/gallery3d/exif/OrderedDataOutputStream.java
+src/com/android/gallery3d/exif/Rational.java
+src/com/android/gallery3d/glrenderer/BasicTexture.java
+src/com/android/gallery3d/glrenderer/BitmapTexture.java
+src/com/android/gallery3d/glrenderer/GLCanvas.java
+src/com/android/gallery3d/glrenderer/GLES20Canvas.java
+src/com/android/gallery3d/glrenderer/GLES20IdImpl.java
+src/com/android/gallery3d/glrenderer/GLId.java
+src/com/android/gallery3d/glrenderer/GLPaint.java
+src/com/android/gallery3d/glrenderer/RawTexture.java
+src/com/android/gallery3d/glrenderer/Texture.java
+src/com/android/gallery3d/glrenderer/UploadedTexture.java
+src/com/android/photos/BitmapRegionTileSource.java
+src/com/android/photos/views/BlockingGLTextureView.java
+src/com/android/photos/views/TiledImageRenderer.java
+src/com/android/photos/views/TiledImageView.java
+src/com/android/gallery3d/common/BitmapUtils.java
+src/com/android/launcher3/CropView.java
+src/com/android/launcher3/WallpaperCropActivity.java
+"""
+
+if len(sys.argv) != 2:
+    print "Usage: python update_sytem_wallpaper_cropper.py <framework_dir>"
+    exit()
+framework_dir = sys.argv[1] + "/packages/WallpaperCropper"
+for file_path in files.split():
+    dir = os.path.dirname(file_path)
+    dir = dir.replace("launcher3", "wallpapercropper")
+    cmd = 'cp %s %s/%s' % (file_path, framework_dir, dir)
+    print cmd
+    os.system(cmd)