Merge "Removing landscape string overrides" into ub-now-queens
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index fa5fe73..0858d6a 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -15,20 +15,20 @@
 -->
 
 <!-- Full screen view projects under the status bar and contains the background -->
-<FrameLayout
+<com.android.launcher3.LauncherRootView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
 
     android:id="@+id/launcher"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@drawable/workspace_bg">
+    android:background="@drawable/workspace_bg"
+    android:fitsSystemWindows="true">
 
     <com.android.launcher3.DragLayer
         android:id="@+id/drag_layer"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:fitsSystemWindows="true">
+        android:layout_height="match_parent">
 
         <com.android.launcher3.FocusIndicatorView
             android:id="@+id/focus_indicator"
@@ -70,4 +70,4 @@
         android:layout_height="match_parent"
         android:inflatedId="@+id/launcher_overlay"
         android:layout="@layout/launcher_overlay" />
-</FrameLayout>
+</com.android.launcher3.LauncherRootView>
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index f2d2dd8..7ba7a89 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -15,14 +15,15 @@
 -->
 
 <!-- Full screen view projects under the status bar and contains the background -->
-<FrameLayout
+<com.android.launcher3.LauncherRootView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
 
     android:id="@+id/launcher"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@drawable/workspace_bg">
+    android:background="@drawable/workspace_bg"
+    android:fitsSystemWindows="true">
 
     <com.android.launcher3.DragLayer
         android:id="@+id/drag_layer"
@@ -90,4 +91,4 @@
         android:layout_height="match_parent"
         android:inflatedId="@+id/launcher_overlay"
         android:layout="@layout/launcher_overlay" />
-</FrameLayout>
+</com.android.launcher3.LauncherRootView>
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 87fa2ed..d4fa2fa 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -15,20 +15,20 @@
 -->
 
 <!-- Full screen view projects under the status bar and contains the background -->
-<FrameLayout
+<com.android.launcher3.LauncherRootView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
 
     android:id="@+id/launcher"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@drawable/workspace_bg">
+    android:background="@drawable/workspace_bg"
+    android:fitsSystemWindows="true">
 
     <com.android.launcher3.DragLayer
         android:id="@+id/drag_layer"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:fitsSystemWindows="true">
+        android:layout_height="match_parent">
 
         <com.android.launcher3.FocusIndicatorView
             android:id="@+id/focus_indicator"
@@ -90,4 +90,4 @@
         android:inflatedId="@+id/launcher_overlay"
         android:layout="@layout/launcher_overlay" />
 
-</FrameLayout>
+</com.android.launcher3.LauncherRootView>
diff --git a/res/layout/drop_target_bar.xml b/res/layout/drop_target_bar.xml
deleted file mode 100644
index f38a500..0000000
--- a/res/layout/drop_target_bar.xml
+++ /dev/null
@@ -1,39 +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.
--->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-    <FrameLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        style="@style/DropTargetButtonContainer"
-        android:layout_weight="1">
-        <!-- Delete target -->
-        <com.android.launcher3.DeleteDropTarget
-            style="@style/DropTargetButton"
-            android:id="@+id/delete_target_text"
-            android:text="@string/delete_zone_label_workspace"
-            android:drawableStart="@drawable/remove_target_selector" />
-    </FrameLayout>
-    <FrameLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        style="@style/DropTargetButtonContainer"
-        android:layout_weight="1">
-        <!-- Info target -->
-        <com.android.launcher3.InfoDropTarget
-            style="@style/DropTargetButton"
-            android:id="@+id/info_target_text"
-            android:text="@string/info_target_label"
-            android:drawableStart="@drawable/info_target_selector" />
-    </FrameLayout>
-</merge>
diff --git a/res/layout/launcher_overlay.xml b/res/layout/launcher_overlay.xml
index 9ef0c9a..b35a2d8 100644
--- a/res/layout/launcher_overlay.xml
+++ b/res/layout/launcher_overlay.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<FrameLayout
+<com.android.launcher3.InsettableFrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent" />
\ No newline at end of file
+    android:layout_height="match_parent" />
diff --git a/res/layout/launcher_overlay_example.xml b/res/layout/launcher_overlay_example.xml
index 422f281..1556b6f 100644
--- a/res/layout/launcher_overlay_example.xml
+++ b/res/layout/launcher_overlay_example.xml
@@ -16,8 +16,10 @@
 
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    launcher:layout_ignoreInsets="true">
 
     <FrameLayout
         android:id="@+id/search_overlay"
diff --git a/res/layout/search_drop_target_bar.xml b/res/layout/search_drop_target_bar.xml
index 2d51b93..af2d0163 100644
--- a/res/layout/search_drop_target_bar.xml
+++ b/res/layout/search_drop_target_bar.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
+<!--
+     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.
@@ -13,21 +14,45 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.SearchDropTargetBar
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="horizontal"
-    android:focusable="false"
+<com.android.launcher3.SearchDropTargetBar xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:focusable="false"
+    android:orientation="horizontal" >
 
     <!-- Drag specific targets container -->
+
     <LinearLayout
         android:id="@+id/drag_target_bar"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_gravity="center">
+        android:layout_gravity="center" >
 
-        <include
-            layout="@layout/drop_target_bar" />
+        <FrameLayout
+            style="@style/DropTargetButtonContainer"
+            android:layout_weight="1" >
+
+            <!-- Delete target -->
+
+            <com.android.launcher3.DeleteDropTarget
+                android:id="@+id/delete_target_text"
+                style="@style/DropTargetButton"
+                android:drawableStart="@drawable/remove_target_selector"
+                android:text="@string/delete_zone_label_workspace" />
+        </FrameLayout>
+
+        <FrameLayout
+            style="@style/DropTargetButtonContainer"
+            android:layout_weight="1" >
+
+            <!-- Info target -->
+
+            <com.android.launcher3.InfoDropTarget
+                android:id="@+id/info_target_text"
+                style="@style/DropTargetButton"
+                android:drawableStart="@drawable/info_target_selector"
+                android:text="@string/info_target_label" />
+        </FrameLayout>
     </LinearLayout>
-</com.android.launcher3.SearchDropTargetBar>
+
+</com.android.launcher3.SearchDropTargetBar>
\ No newline at end of file
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 8aa65a4..5d01f36 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"Program is nie geïnstalleer nie."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Program is nie beskikbaar nie"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Afgelaaide program in veiligmodus gedeaktiveer"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Legstukke gedeaktiveer in Veiligmodus"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Legstukke"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Legstukke"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Wys Mem"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 4b05436..95125bc 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"Aplikace není nainstalována."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Aplikace není k dispozici."</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Stažená aplikace je v nouzovém režimu zakázána"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"V Bezpečném režimu jsou widgety zakázány."</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Widgety"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Widgety"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Zobrazit Mem"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index d5352f2..c3bb088 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"Appen er ikke installeret."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Appen er ikke tilgængelig"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloadet app er deaktiveret i sikker tilstand"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets er deaktiveret i Beskyttet tilstand"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Vis Mem"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index dc43f2d..847faac 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"No se instaló la aplicación."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"La aplicación no está disponible."</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicación descargada inhabilitada en modo seguro"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets inhabilitados en modo seguro"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostrar memoria"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index c765d8f..ce10c2d 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"برنامه نصب نشده است."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"برنامه در دسترس نیست"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"برنامه دانلود شده در حالت ایمن غیرفعال شد"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"ابزارک‌ها در حالت ایمن غیرفعال هستند"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"ابزارک‌ها"</string>
     <string name="widget_adder" msgid="3201040140710381657">"ابزارک‌ها"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"‏نمایش Mem"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index c1070f2..a94f571 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"L\'application n\'est pas installée."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Application indisponible"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'application téléchargée est désactivée en mode sécurisé."</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets désactivés en mode sans échec"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Afficher la mémoire"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 064d751..0f1aac4 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"L\'application n\'est pas installée."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Application indisponible"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'application téléchargée est désactivée en mode sécurisé."</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Les widgets sont désactivés en mode sécurisé."</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Afficher la mémoire"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 9402a42..dcff15c 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"Ծրագիրը տեղադրված չէ:"</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Հավելվածը հասանելի չէ"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Ներբեռնված ծրագիրն անջատված է Անվտանգ ռեժիմում"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Վիջեթներն անջատված են անվտանգ ռեժիմում"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Վիջեթներ"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Վիջեթներ"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Ցուցադրել մեմը"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 8092331..823d36a 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"აპი არ არის დაყენებული."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"აპი მიუწვდომელია"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"უსაფრთხო რეჟიმში ჩამოტვირთული აპი გაუქმებულია"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"უსაფრთხო რეჟიმში ვიჯეტი გამორთულია"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"ვიჯეტები"</string>
     <string name="widget_adder" msgid="3201040140710381657">"ვიჯეტები"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mem-ის ჩვენება"</string>
diff --git a/res/values-land/styles.xml b/res/values-land/styles.xml
index ac210d7..8a255c9 100644
--- a/res/values-land/styles.xml
+++ b/res/values-land/styles.xml
@@ -18,27 +18,25 @@
 -->
 
 <resources>
-<!-- Search Bar -->
-    <style name="SearchButton">
-    </style>
+
+    <!-- Search Bar -->
+    <style name="SearchButton"></style>
+
     <style name="DropTargetButtonContainer">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
     </style>
-    <style name="DropTargetButton">
-        <item name="android:layout_width">wrap_content</item>
+
+    <!-- This style applies to the drop target when it is shown in the sidebar -->
+    <style name="DropTargetButton" parent="DropTargetButton.Base">
         <item name="android:layout_height">wrap_content</item>
-        <item name="android:layout_gravity">center</item>
         <item name="android:gravity">center</item>
+        <item name="android:drawablePadding">0dp</item>
         <item name="android:paddingTop">@dimen/toolbar_button_vertical_padding</item>
         <item name="android:paddingBottom">@dimen/toolbar_button_vertical_padding</item>
         <item name="android:paddingLeft">@dimen/toolbar_button_horizontal_padding</item>
         <item name="android:paddingRight">@dimen/toolbar_button_horizontal_padding</item>
         <item name="android:shadowColor">#DD000000</item>
-        <item name="android:shadowDx">0.0</item>
-        <item name="android:shadowDy">1.0</item>
-        <item name="android:shadowRadius">4.0</item>
     </style>
 
-</resources>
-
+</resources>
\ No newline at end of file
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index 935e997..ac47503 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"ແອັບຯບໍ່ໄດ້ຖືກຕິດຕັ້ງ."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"ແອັບຯ​ໃຊ້​ບໍ່​ໄດ້"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ແອັບຯ​ທີ່​ດາວ​ໂຫລດ​ແລ້ວ​ຖືກ​ປິດ​ການ​ນຳ​ໃຊ້​ໃນ Safe mode"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"​ວິດ​ເຈັດ​ຖືກ​ປິດ​ໃນ Safe mode"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"ວິດເຈັດ"</string>
     <string name="widget_adder" msgid="3201040140710381657">"ວິດເຈັດ"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"ສະແດງຄວາມຈຳ"</string>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 1770e89..5f96ead 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"Apl tidak dipasang."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Apl tidak tersedia"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Apl yang dimuat turun dilumpuhkan dalam mod Selamat"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Widget dilumpuhkan dalam mod Selamat"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Widget"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Widget"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Papar Mem"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 6211361..a7faeaf 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"Appen er ikke installert."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Appen er ikke tilgjengelig"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"En nedlastet app er deaktivert i sikker modus"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Moduler er deaktivert i sikker modus"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Moduler"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Moduler"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Vis minne"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 355e71a..6c9a0d9 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"O app não está instalado."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"O app não está disponível"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"App transferido por download desativado no modo de segurança"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets desativados no modo de segurança"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostrar memória"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 7cbd9fa..8b9d706 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"Приложение удалено"</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Приложение недоступно"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Скачанное приложение отключено в безопасном режиме"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Виджеты отключены в безопасном режиме"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Виджеты"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Виджеты"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Сведения о памяти"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index e3c242d..2ffa4b4 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"Aplikácia nie je nainštalovaná."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Aplikácia nie je k dispozícii"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Stiahnutá aplikácia je v núdzovom režime zakázaná"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Miniaplikácie sú v núdzovom režime zakázané"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Miniaplikácie"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Miniaplikácie"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Zobraziť pamäť"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 807b1b4..f02ea27 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"Aplikacija ni nameščena."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Aplikacija ni na voljo"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Prenesena aplikacija je onemogočena v Varnem načinu"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Pripomočki so onemogočeni v varnem načinu"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Pripomočki"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Pripomočki"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Pokaži pomnilnik"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 0284679..05df3c4 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"Programu haijasakinishwa."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Programu haipatikani"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Programu iliyopakuliwa imezimwa katika Hali Salama"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Wijeti zimezimwa katika hali ya Usalama"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Wijeti"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Wijeti"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Onyesha Kumbukumbu"</string>
diff --git a/res/values-sw340dp/dimens.xml b/res/values-sw340dp/dimens.xml
index 69d6e58..c9f2981 100644
--- a/res/values-sw340dp/dimens.xml
+++ b/res/values-sw340dp/dimens.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
+<!--
+     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.
@@ -15,6 +16,9 @@
 -->
 
 <resources>
+
     <!-- Drag padding to add to the bottom of drop targets -->
     <dimen name="drop_target_drag_padding">20dp</dimen>
-</resources>
+    <dimen name="drop_target_text_size">16sp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/res/values-sw720dp/styles.xml b/res/values-sw720dp/styles.xml
index 4ec04d9..cbc1e29 100644
--- a/res/values-sw720dp/styles.xml
+++ b/res/values-sw720dp/styles.xml
@@ -18,26 +18,22 @@
 -->
 
 <resources>
-<!-- Workspace -->
-    <style name="SearchButton">
-    </style>
+
+    <!-- Workspace -->
+    <style name="SearchButton"></style>
+
     <style name="DropTargetButtonContainer">
         <item name="android:layout_width">0dp</item>
         <item name="android:layout_height">match_parent</item>
     </style>
-    <style name="DropTargetButton">
-        <item name="android:layout_width">wrap_content</item>
-        <item name="android:layout_height">match_parent</item>
-        <item name="android:layout_gravity">center</item>
-        <item name="android:gravity">center_vertical</item>
-        <item name="android:drawablePadding">7.5dp</item>
+
+    <style name="DropTargetButton" parent="DropTargetButton.Base">
         <item name="android:paddingLeft">60dp</item>
         <item name="android:paddingRight">60dp</item>
-        <item name="android:textColor">#FFFFFFFF</item>
-        <item name="android:textSize">16sp</item>
         <item name="android:shadowColor">#393939</item>
         <item name="android:shadowDx">0.0</item>
         <item name="android:shadowDy">0.0</item>
         <item name="android:shadowRadius">2.0</item>
     </style>
-</resources>
+
+</resources>
\ No newline at end of file
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index b71748e..341def7 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"Uygulama yüklü değil."</string>
     <string name="activity_not_available" msgid="7456344436509528827">"Uygulama kullanılamıyor"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"İndirilen uygulama Güvenli modda devre dışı bırakıldı"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"Güvenli modda widget\'lar devre dışı"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"Widget\'lar"</string>
     <string name="widget_adder" msgid="3201040140710381657">"Widget\'lar"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"Belleği Göster"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 30e34b9..e716fde 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -26,8 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"未安装该应用。"</string>
     <string name="activity_not_available" msgid="7456344436509528827">"应用不可用"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"安全模式下不允许使用下载的此应用"</string>
-    <!-- no translation found for safemode_widget_error (4863470563535682004) -->
-    <skip />
+    <string name="safemode_widget_error" msgid="4863470563535682004">"安全模式下不允许使用小部件"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"小部件"</string>
     <string name="widget_adder" msgid="3201040140710381657">"小部件"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"显示内存空间"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 3656e2e..02c90f2 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -26,7 +26,7 @@
     <string name="activity_not_found" msgid="8071924732094499514">"尚未安裝應用程式。"</string>
     <string name="activity_not_available" msgid="7456344436509528827">"目前無法使用這個應用程式"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"在安全模式中無法使用「已下載的應用程式」功能"</string>
-    <string name="safemode_widget_error" msgid="4863470563535682004">"在安全模式下無法使用小工具"</string>
+    <string name="safemode_widget_error" msgid="4863470563535682004">"在安全模式中無法使用小工具"</string>
     <string name="widgets_tab_label" msgid="2921133187116603919">"小工具"</string>
     <string name="widget_adder" msgid="3201040140710381657">"小工具"</string>
     <string name="toggle_weight_watcher" msgid="5645299835184636119">"顯示記憶體"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index b4e1543..a52bbe2 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -145,4 +145,8 @@
         <attr name="ringOutset" format="dimension" />
         <attr name="indicatorSize" format="dimension" />
     </declare-styleable>
+
+    <declare-styleable name="InsettableFrameLayout_Layout">
+        <attr name="layout_ignoreInsets" format="boolean" />
+    </declare-styleable>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 2c9e689..20bc7cc 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -62,6 +62,7 @@
 
     <!-- Drag padding to add to the bottom of drop targets -->
     <dimen name="drop_target_drag_padding">14dp</dimen>
+    <dimen name="drop_target_text_size">14sp</dimen>
 
 <!-- Dragging -->
     <!-- the area at the edge of the screen that makes the workspace go left
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a6397e3..b3abe8f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -125,11 +125,11 @@
          device. [CHAR_LIMIT=30]-->
     <string name="delete_zone_label_all_apps">Uninstall</string>
 
-    <!-- Label for delete drop target. [CHAR_LIMIT=30] -->
+    <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
     <string name="delete_target_label">Remove</string>
-    <!-- Label for uninstall drop target. [CHAR_LIMIT=30]-->
+    <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
     <string name="delete_target_uninstall_label">Uninstall</string>
-    <!-- Label for the info icon. [CHAR_LIMIT=30] -->
+    <!-- Label for the info icon. [CHAR_LIMIT=20] -->
     <string name="info_target_label">App info</string>
 
     <!-- Accessibility: Search button -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 873b74c..15415c7 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -32,10 +32,9 @@
         <item name="android:fontFamily">sans-serif-condensed</item>
     </style>
 
-    <style name="WorkspaceIcon.Portrait">
-    </style>
-    <style name="WorkspaceIcon.Landscape">
-    </style>
+    <style name="WorkspaceIcon.Portrait"></style>
+
+    <style name="WorkspaceIcon.Landscape"></style>
 
     <style name="WorkspaceIcon.AppsCustomize">
         <item name="android:background">@null</item>
@@ -52,15 +51,16 @@
         <item name="customShadows">false</item>
     </style>
 
-    <style name="SearchDropTargetBar">
-    </style>
-    <style name="SearchButton">
-    </style>
+    <style name="SearchDropTargetBar"></style>
+
+    <style name="SearchButton"></style>
+
     <style name="DropTargetButtonContainer">
         <item name="android:layout_width">0dp</item>
         <item name="android:layout_height">match_parent</item>
     </style>
-    <style name="DropTargetButton">
+
+    <style name="DropTargetButton.Base">
         <item name="android:layout_width">wrap_content</item>
         <item name="android:layout_height">match_parent</item>
         <item name="android:layout_gravity">center</item>
@@ -69,7 +69,7 @@
         <item name="android:paddingLeft">25dp</item>
         <item name="android:paddingRight">25dp</item>
         <item name="android:textColor">#FFFFFFFF</item>
-        <item name="android:textSize">16sp</item>
+        <item name="android:textSize">@dimen/drop_target_text_size</item>
         <item name="android:singleLine">true</item>
         <item name="android:ellipsize">end</item>
         <item name="android:shadowColor">#FF000000</item>
@@ -78,6 +78,8 @@
         <item name="android:shadowRadius">4.0</item>
     </style>
 
+    <style name="DropTargetButton" parent="DropTargetButton.Base"></style>
+
     <style name="PreloadIcon">
         <item name="background">@drawable/virtual_preload</item>
         <item name="indicatorSize">4dp</item>
@@ -94,7 +96,9 @@
     <style name="PagedViewWidgetImageView">
         <item name="android:paddingLeft">@dimen/app_widget_preview_padding_left</item>
     </style>
+
     <style name="SearchButton.WithPaddingStart">
         <item name="android:paddingLeft">8dp</item>
     </style>
+
 </resources>
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index ff9072a..79d4278 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -43,7 +43,7 @@
 /**
  * A ViewGroup that coordinates dragging across its descendants
  */
-public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChangeListener {
+public class DragLayer extends InsettableFrameLayout {
     private DragController mDragController;
     private int[] mTmpXY = new int[2];
 
@@ -71,8 +71,6 @@
 
     private TouchCompleteListener mTouchCompleteListener;
 
-    private final Rect mInsets = new Rect();
-
     private View mOverlayView;
     private int mTopViewIndex;
     private int mChildCountOnLastUpdate = -1;
@@ -103,7 +101,6 @@
         // Disable multitouch across the workspace/all apps/customize tray
         setMotionEventSplittingEnabled(false);
         setChildrenDrawingOrderEnabled(true);
-        setOnHierarchyChangeListener(this);
 
         final Resources res = getResources();
         mLeftHoverDrawable = res.getDrawable(R.drawable.page_hover_left);
@@ -123,27 +120,6 @@
         return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
     }
 
-    @Override
-    protected boolean fitSystemWindows(Rect insets) {
-        final int n = getChildCount();
-        for (int i = 0; i < n; i++) {
-            final View child = getChildAt(i);
-            setInsets(child, insets, mInsets);
-        }
-        mInsets.set(insets);
-        return true; // I'll take it from here
-    }
-
-    Rect getInsets() {
-        return mInsets;
-    }
-
-    @Override
-    public void addView(View child, int index, android.view.ViewGroup.LayoutParams params) {
-        super.addView(child, index, params);
-        setInsets(child, mInsets, new Rect());
-    }
-
     public void showOverlayView(View overlayView) {
         LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
         mOverlayView = overlayView;
@@ -158,19 +134,6 @@
         removeView(mOverlayView);
     }
 
-    private void setInsets(View child, Rect newInsets, Rect oldInsets) {
-        final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams();
-        if (child instanceof Insettable) {
-            ((Insettable) child).setInsets(newInsets);
-        } else {
-            flp.topMargin += (newInsets.top - oldInsets.top);
-            flp.leftMargin += (newInsets.left - oldInsets.left);
-            flp.rightMargin += (newInsets.right - oldInsets.right);
-            flp.bottomMargin += (newInsets.bottom - oldInsets.bottom);
-        }
-        child.setLayoutParams(flp);
-    }
-
     private boolean isEventOverFolderTextRegion(Folder folder, MotionEvent ev) {
         getDescendantRectRelativeToSelf(folder.getEditTextRegion(), mHitRect);
         if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
@@ -815,6 +778,7 @@
 
     @Override
     public void onChildViewAdded(View parent, View child) {
+        super.onChildViewAdded(parent, child);
         if (mOverlayView != null) {
             // ensure that the overlay view stays on top. we can't use drawing order for this
             // because in API level 16 touch dispatch doesn't respect drawing order.
diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
new file mode 100644
index 0000000..1c3d5a1
--- /dev/null
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -0,0 +1,96 @@
+package com.android.launcher3;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+public class InsettableFrameLayout extends FrameLayout implements
+    ViewGroup.OnHierarchyChangeListener, Insettable {
+
+    protected Rect mInsets = new Rect();
+
+    public InsettableFrameLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setOnHierarchyChangeListener(this);
+    }
+
+    public void setFrameLayoutChildInsets(View child, Rect newInsets, Rect oldInsets) {
+        final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+        if (child instanceof Insettable) {
+            ((Insettable) child).setInsets(newInsets);
+        } else if (!lp.ignoreInsets) {
+            lp.topMargin += (newInsets.top - oldInsets.top);
+            lp.leftMargin += (newInsets.left - oldInsets.left);
+            lp.rightMargin += (newInsets.right - oldInsets.right);
+            lp.bottomMargin += (newInsets.bottom - oldInsets.bottom);
+        }
+        child.setLayoutParams(lp);
+    }
+
+    @Override
+    public void setInsets(Rect insets) {
+        final int n = getChildCount();
+        for (int i = 0; i < n; i++) {
+            final View child = getChildAt(i);
+            setFrameLayoutChildInsets(child, insets, mInsets);
+        }
+        mInsets.set(insets);
+    }
+
+    @Override
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new InsettableFrameLayout.LayoutParams(getContext(), attrs);
+    }
+
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+    }
+
+    // Override to allow type-checking of LayoutParams.
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof InsettableFrameLayout.LayoutParams;
+    }
+
+    @Override
+    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+        return new LayoutParams(p);
+    }
+
+    class LayoutParams extends FrameLayout.LayoutParams {
+        boolean ignoreInsets = false;
+
+        public LayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+            TypedArray a = c.obtainStyledAttributes(attrs,
+                    R.styleable.InsettableFrameLayout_Layout);
+            ignoreInsets = a.getBoolean(
+                    R.styleable.InsettableFrameLayout_Layout_layout_ignoreInsets, false);
+            a.recycle();
+        }
+
+        public LayoutParams(int width, int height) {
+            super(width, height);
+        }
+
+        public LayoutParams(ViewGroup.LayoutParams lp) {
+            super(lp);
+        }
+    }
+
+    @Override
+    public void onChildViewAdded(View parent, View child) {
+        setFrameLayoutChildInsets(child, mInsets, new Rect());
+    }
+
+    @Override
+    public void onChildViewRemoved(View parent, View child) {
+    }
+
+}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 309837f..b357e7a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -231,7 +231,7 @@
 
     LauncherOverlayCallbacks mLauncherOverlayCallbacks = new LauncherOverlayCallbacksImpl();
     LauncherOverlay mLauncherOverlay;
-    ViewGroup mLauncherOverlayView;
+    InsettableFrameLayout mLauncherOverlayContainer;
 
     static final int APPWIDGET_HOST_ID = 1024;
     public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
@@ -514,9 +514,9 @@
             mLauncherCallbacks.onCreate(savedInstanceState);
             if (mLauncherCallbacks.hasLauncherOverlay()) {
                 ViewStub stub = (ViewStub) findViewById(R.id.launcher_overlay_stub);
-                mLauncherOverlayView = (ViewGroup) stub.inflate();
-                mLauncherOverlay = mLauncherCallbacks.setLauncherOverlayView(mLauncherOverlayView,
-                        mLauncherOverlayCallbacks);
+                mLauncherOverlayContainer = (InsettableFrameLayout) stub.inflate();
+                mLauncherOverlay = mLauncherCallbacks.setLauncherOverlayView(
+                        mLauncherOverlayContainer, mLauncherOverlayCallbacks);
                 mWorkspace.setLauncherOverlay(mLauncherOverlay);
             }
         }
@@ -3741,14 +3741,14 @@
                     yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1];
                     xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0];
                 } else {
-                    yDrift = 5 * height / 4;
+                    yDrift = 2 * height / 3;
                     xDrift = 0;
                 }
 
                 revealView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                 TimeInterpolator decelerateInterpolator = material ?
                         new LogDecelerateInterpolator(100, 0) :
-                        new LogDecelerateInterpolator(30, 0);
+                        new DecelerateInterpolator(1f);
 
                 // The vertical motion of the apps panel should be delayed by one frame
                 // from the conceal animation in order to give the right feel. We correpsondingly
@@ -3772,9 +3772,9 @@
                     revealView.setAlpha(1f);
                     ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha",
                             1f, finalAlpha);
-                    panelAlpha.setDuration(revealDuration);
-                    panelAlpha.setInterpolator(material ? decelerateInterpolator :
-                        new AccelerateInterpolator(1.5f));
+                    panelAlpha.setDuration(material ? revealDuration : 150);
+                    panelAlpha.setInterpolator(decelerateInterpolator);
+                    panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
                     mStateAnimation.play(panelAlpha);
                 }
 
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
index c20c693..09ad22b 100644
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -78,6 +78,12 @@
     @Override
     public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
             throws IOException {
+        if (!Utilities.isLmpOrAbove()) {
+            // No restore for old devices.
+            Log.i(TAG, "You shall not pass!!!");
+            Log.d(TAG, "Restore is only supported on devices running Lollipop and above.");
+            return;
+        }
         super.onRestore(data, appVersionCode, newState);
 
         // If no favorite was migrated, clear the data and start fresh.
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 201f3e9..b2ac577 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -15,22 +15,6 @@
  */
 package com.android.launcher3;
 
-import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
-import com.google.protobuf.nano.MessageNano;
-
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.LauncherSettings.WorkspaceScreens;
-import com.android.launcher3.backup.BackupProtos;
-import com.android.launcher3.backup.BackupProtos.CheckedMessage;
-import com.android.launcher3.backup.BackupProtos.Favorite;
-import com.android.launcher3.backup.BackupProtos.Journal;
-import com.android.launcher3.backup.BackupProtos.Key;
-import com.android.launcher3.backup.BackupProtos.Resource;
-import com.android.launcher3.backup.BackupProtos.Screen;
-import com.android.launcher3.backup.BackupProtos.Widget;
-import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.compat.UserHandleCompat;
-
 import android.app.backup.BackupDataInputStream;
 import android.app.backup.BackupDataOutput;
 import android.app.backup.BackupHelper;
@@ -42,6 +26,7 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -51,6 +36,21 @@
 import android.util.Base64;
 import android.util.Log;
 
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherSettings.WorkspaceScreens;
+import com.android.launcher3.backup.BackupProtos;
+import com.android.launcher3.backup.BackupProtos.CheckedMessage;
+import com.android.launcher3.backup.BackupProtos.Favorite;
+import com.android.launcher3.backup.BackupProtos.Journal;
+import com.android.launcher3.backup.BackupProtos.Key;
+import com.android.launcher3.backup.BackupProtos.Resource;
+import com.android.launcher3.backup.BackupProtos.Screen;
+import com.android.launcher3.backup.BackupProtos.Widget;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+import com.google.protobuf.nano.MessageNano;
+
 import java.io.ByteArrayOutputStream;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
@@ -60,7 +60,6 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 import java.util.zip.CRC32;
 
 /**
@@ -71,7 +70,6 @@
     private static final String TAG = "LauncherBackupHelper";
     private static final boolean VERBOSE = LauncherBackupAgentHelper.VERBOSE;
     private static final boolean DEBUG = LauncherBackupAgentHelper.DEBUG;
-    private static final boolean DEBUG_PAYLOAD = false;
 
     private static final int MAX_JOURNAL_SIZE = 1000000;
 
@@ -90,27 +88,25 @@
     private static final Bitmap.CompressFormat IMAGE_FORMAT =
             android.graphics.Bitmap.CompressFormat.PNG;
 
-    private static BackupManager sBackupManager;
-
     private static final String[] FAVORITE_PROJECTION = {
-            Favorites._ID,                     // 0
-            Favorites.MODIFIED,                // 1
-            Favorites.INTENT,                  // 2
-            Favorites.APPWIDGET_PROVIDER,      // 3
-            Favorites.APPWIDGET_ID,            // 4
-            Favorites.CELLX,                   // 5
-            Favorites.CELLY,                   // 6
-            Favorites.CONTAINER,               // 7
-            Favorites.ICON,                    // 8
-            Favorites.ICON_PACKAGE,            // 9
-            Favorites.ICON_RESOURCE,           // 10
-            Favorites.ICON_TYPE,               // 11
-            Favorites.ITEM_TYPE,               // 12
-            Favorites.SCREEN,                  // 13
-            Favorites.SPANX,                   // 14
-            Favorites.SPANY,                   // 15
-            Favorites.TITLE,                   // 16
-            Favorites.PROFILE_ID,              // 17
+        Favorites._ID,                     // 0
+        Favorites.MODIFIED,                // 1
+        Favorites.INTENT,                  // 2
+        Favorites.APPWIDGET_PROVIDER,      // 3
+        Favorites.APPWIDGET_ID,            // 4
+        Favorites.CELLX,                   // 5
+        Favorites.CELLY,                   // 6
+        Favorites.CONTAINER,               // 7
+        Favorites.ICON,                    // 8
+        Favorites.ICON_PACKAGE,            // 9
+        Favorites.ICON_RESOURCE,           // 10
+        Favorites.ICON_TYPE,               // 11
+        Favorites.ITEM_TYPE,               // 12
+        Favorites.SCREEN,                  // 13
+        Favorites.SPANX,                   // 14
+        Favorites.SPANY,                   // 15
+        Favorites.TITLE,                   // 16
+        Favorites.PROFILE_ID,              // 17
     };
 
     private static final int ID_INDEX = 0;
@@ -130,37 +126,46 @@
     private static final int SPANX_INDEX = 14;
     private static final int SPANY_INDEX = 15;
     private static final int TITLE_INDEX = 16;
-    private static final int PROFILE_ID_INDEX = 17;
 
     private static final String[] SCREEN_PROJECTION = {
-            WorkspaceScreens._ID,              // 0
-            WorkspaceScreens.MODIFIED,         // 1
-            WorkspaceScreens.SCREEN_RANK       // 2
+        WorkspaceScreens._ID,              // 0
+        WorkspaceScreens.MODIFIED,         // 1
+        WorkspaceScreens.SCREEN_RANK       // 2
     };
 
     private static final int SCREEN_RANK_INDEX = 2;
 
-    private static IconCache mIconCache;
-
     private final Context mContext;
-
-    private final boolean mRestoreEnabled;
-
-    private HashMap<ComponentName, AppWidgetProviderInfo> mWidgetMap;
-
+    private final HashSet<String> mExistingKeys;
     private final ArrayList<Key> mKeys;
 
+    private IconCache mIconCache;
+    private BackupManager mBackupManager;
+    private HashMap<ComponentName, AppWidgetProviderInfo> mWidgetMap;
+    private byte[] mBuffer = new byte[512];
+    private long mLastBackupRestoreTime;
+
     public LauncherBackupHelper(Context context, boolean restoreEnabled) {
         mContext = context;
-        mRestoreEnabled = restoreEnabled;
+        mExistingKeys = new HashSet<String>();
         mKeys = new ArrayList<Key>();
     }
 
     private void dataChanged() {
-        if (sBackupManager == null) {
-            sBackupManager = new BackupManager(mContext);
+        if (mBackupManager == null) {
+            mBackupManager = new BackupManager(mContext);
         }
-        sBackupManager.dataChanged();
+        mBackupManager.dataChanged();
+    }
+
+    private void applyJournal(Journal journal) {
+        mLastBackupRestoreTime = journal.t;
+        mExistingKeys.clear();
+        if (journal.key != null) {
+            for (Key key : journal.key) {
+                mExistingKeys.add(keyToBackupKey(key));
+            }
+        }
     }
 
     /**
@@ -181,32 +186,45 @@
         if (VERBOSE) Log.v(TAG, "onBackup");
 
         Journal in = readJournal(oldState);
-        Journal out = new Journal();
+        if (!launcherIsReady()) {
+            // Perform backup later.
+            writeJournal(newState, in);
+            return;
+        }
+        Log.v(TAG, "lastBackupTime = " + in.t);
+        mKeys.clear();
+        applyJournal(in);
 
-        long lastBackupTime = in.t;
-        out.t = System.currentTimeMillis();
-        out.rows = 0;
-        out.bytes = 0;
+        // Record the time before performing backup so that entries edited while the backup
+        // was going on, do not get missed in next backup.
+        long newBackupTime = System.currentTimeMillis();
 
-        Log.v(TAG, "lastBackupTime = " + lastBackupTime);
+        try {
+            backupFavorites(data);
+            backupScreens(data);
+            backupIcons(data);
+            backupWidgets(data);
 
-        ArrayList<Key> keys = new ArrayList<Key>();
-        if (launcherIsReady()) {
-            try {
-                backupFavorites(in, data, out, keys);
-                backupScreens(in, data, out, keys);
-                backupIcons(in, data, out, keys);
-                backupWidgets(in, data, out, keys);
-            } catch (IOException e) {
-                Log.e(TAG, "launcher backup has failed", e);
+            // Delete any key which still exist in the old backup, but is not valid anymore.
+            HashSet<String> validKeys = new HashSet<String>();
+            for (Key key : mKeys) {
+                validKeys.add(keyToBackupKey(key));
             }
-            out.key = keys.toArray(new BackupProtos.Key[keys.size()]);
-        } else {
-            out = in;
+            mExistingKeys.removeAll(validKeys);
+
+            // Delete anything left in the existing keys.
+            for (String deleted: mExistingKeys) {
+                if (VERBOSE) Log.v(TAG, "dropping deleted item " + deleted);
+                data.writeEntityHeader(deleted, -1);
+            }
+
+            mExistingKeys.clear();
+            mLastBackupRestoreTime = newBackupTime;
+        } catch (IOException e) {
+            Log.e(TAG, "launcher backup has failed", e);
         }
 
-        writeJournal(newState, out);
-        Log.v(TAG, "onBackup: wrote " + out.bytes + "b in " + out.rows + " rows.");
+        writeNewStateDescription(newState);
     }
 
     /**
@@ -218,49 +236,41 @@
      */
     @Override
     public void restoreEntity(BackupDataInputStream data) {
-        if (VERBOSE) Log.v(TAG, "restoreEntity");
-        byte[] buffer = new byte[512];
-            String backupKey = data.getKey();
-            int dataSize = data.size();
-            if (buffer.length < dataSize) {
-                buffer = new byte[dataSize];
-            }
-            Key key = null;
-        int bytesRead = 0;
-        try {
-            bytesRead = data.read(buffer, 0, dataSize);
-            if (DEBUG) Log.d(TAG, "read " + bytesRead + " of " + dataSize + " available");
-        } catch (IOException e) {
-            Log.e(TAG, "failed to read entity from restore data", e);
+        int dataSize = data.size();
+        if (mBuffer.length < dataSize) {
+            mBuffer = new byte[dataSize];
         }
         try {
-            key = backupKeyToKey(backupKey);
+            int bytesRead = data.read(mBuffer, 0, dataSize);
+            if (DEBUG) Log.d(TAG, "read " + bytesRead + " of " + dataSize + " available");
+            String backupKey = data.getKey();
+            Key key = backupKeyToKey(backupKey);
             mKeys.add(key);
             switch (key.type) {
                 case Key.FAVORITE:
-                    restoreFavorite(key, buffer, dataSize, mKeys);
+                    restoreFavorite(key, mBuffer, dataSize);
                     break;
 
                 case Key.SCREEN:
-                    restoreScreen(key, buffer, dataSize, mKeys);
+                    restoreScreen(key, mBuffer, dataSize);
                     break;
 
                 case Key.ICON:
-                    restoreIcon(key, buffer, dataSize, mKeys);
+                    restoreIcon(key, mBuffer, dataSize);
                     break;
 
                 case Key.WIDGET:
-                    restoreWidget(key, buffer, dataSize, mKeys);
+                    restoreWidget(key, mBuffer, dataSize);
                     break;
 
                 default:
                     Log.w(TAG, "unknown restore entity type: " + key.type);
+                    mKeys.remove(key);
                     break;
             }
-        } catch (KeyParsingException e) {
-            Log.w(TAG, "ignoring unparsable backup key: " + backupKey);
+        } catch (IOException e) {
+            Log.w(TAG, "ignoring unparsable backup entry", e);
         }
-
     }
 
     /**
@@ -270,63 +280,55 @@
      */
     @Override
     public void writeNewStateDescription(ParcelFileDescriptor newState) {
-        // clear the output journal time, to force a full backup to
-        // will catch any changes the restore process might have made
-        Journal out = new Journal();
-        out.t = 0;
-        out.key = mKeys.toArray(new BackupProtos.Key[mKeys.size()]);
-        writeJournal(newState, out);
-        Log.v(TAG, "onRestore: read " + mKeys.size() + " rows");
-        mKeys.clear();
+        writeJournal(newState, getCurrentStateJournal());
+    }
+
+    private Journal getCurrentStateJournal() {
+        Journal journal = new Journal();
+        journal.t = mLastBackupRestoreTime;
+        journal.key = mKeys.toArray(new BackupProtos.Key[mKeys.size()]);
+        journal.appVersion = getAppVersion();
+        return journal;
+    }
+
+    private int getAppVersion() {
+        try {
+            return mContext.getPackageManager()
+                    .getPackageInfo(mContext.getPackageName(), 0).versionCode;
+        } catch (NameNotFoundException e) {
+            return 0;
+        }
     }
 
     /**
      * Write all modified favorites to the data stream.
      *
-     *
-     * @param in notes from last backup
      * @param data output stream for key/value pairs
-     * @param out notes about this backup
-     * @param keys keys to mark as clean in the notes for next backup
      * @throws IOException
      */
-    private void backupFavorites(Journal in, BackupDataOutput data, Journal out,
-            ArrayList<Key> keys)
-            throws IOException {
-        // read the old ID set
-        Set<String> savedIds = getSavedIdsByType(Key.FAVORITE, in);
-        if (DEBUG) Log.d(TAG, "favorite savedIds.size()=" + savedIds.size());
-
+    private void backupFavorites(BackupDataOutput data) throws IOException {
         // persist things that have changed since the last backup
         ContentResolver cr = mContext.getContentResolver();
         // Don't backup apps in other profiles for now.
         Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
                 getUserSelectionArg(), null, null);
-        Set<String> currentIds = new HashSet<String>(cursor.getCount());
         try {
             cursor.moveToPosition(-1);
             while(cursor.moveToNext()) {
                 final long id = cursor.getLong(ID_INDEX);
                 final long updateTime = cursor.getLong(ID_MODIFIED);
                 Key key = getKey(Key.FAVORITE, id);
-                keys.add(key);
+                mKeys.add(key);
                 final String backupKey = keyToBackupKey(key);
-                currentIds.add(backupKey);
-                if (!savedIds.contains(backupKey) || updateTime >= in.t) {
-                    byte[] blob = packFavorite(cursor);
-                    writeRowToBackup(key, blob, out, data);
+                if (!mExistingKeys.contains(backupKey) || updateTime >= mLastBackupRestoreTime) {
+                    writeRowToBackup(key, packFavorite(cursor), data);
                 } else {
-                    if (VERBOSE) Log.v(TAG, "favorite " + id + " was too old: " + updateTime);
+                    if (DEBUG) Log.d(TAG, "favorite already backup up: " + id);
                 }
             }
         } finally {
             cursor.close();
         }
-        if (DEBUG) Log.d(TAG, "favorite currentIds.size()=" + currentIds.size());
-
-        // these IDs must have been deleted
-        savedIds.removeAll(currentIds);
-        out.rows += removeDeletedKeysFromBackup(savedIds, data);
     }
 
     /**
@@ -337,74 +339,46 @@
      * @param key identifier for the row
      * @param buffer the serialized proto from the stream, may be larger than dataSize
      * @param dataSize the size of the proto from the stream
-     * @param keys keys to mark as clean in the notes for next backup
      */
-    private void restoreFavorite(Key key, byte[] buffer, int dataSize, ArrayList<Key> keys) {
+    private void restoreFavorite(Key key, byte[] buffer, int dataSize) throws IOException {
         if (VERBOSE) Log.v(TAG, "unpacking favorite " + key.id);
         if (DEBUG) Log.d(TAG, "read (" + buffer.length + "): " +
                 Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP));
 
-        if (!mRestoreEnabled) {
-            if (VERBOSE) Log.v(TAG, "restore not enabled: skipping database mutation");
-            return;
-        }
-
-        try {
-            ContentResolver cr = mContext.getContentResolver();
-            ContentValues values = unpackFavorite(buffer, 0, dataSize);
-            cr.insert(Favorites.CONTENT_URI_NO_NOTIFICATION, values);
-        } catch (InvalidProtocolBufferNanoException e) {
-            Log.e(TAG, "failed to decode favorite", e);
-        }
+        ContentResolver cr = mContext.getContentResolver();
+        ContentValues values = unpackFavorite(buffer, dataSize);
+        cr.insert(Favorites.CONTENT_URI_NO_NOTIFICATION, values);
     }
 
     /**
      * Write all modified screens to the data stream.
      *
-     *
-     * @param in notes from last backup
      * @param data output stream for key/value pairs
-     * @param out notes about this backup
-     * @param keys keys to mark as clean in the notes for next backup
      * @throws IOException
      */
-    private void backupScreens(Journal in, BackupDataOutput data, Journal out,
-            ArrayList<Key> keys)
-            throws IOException {
-        // read the old ID set
-        Set<String> savedIds = getSavedIdsByType(Key.SCREEN, in);
-        if (DEBUG) Log.d(TAG, "screen savedIds.size()=" + savedIds.size());
-
+    private void backupScreens(BackupDataOutput data) throws IOException {
         // persist things that have changed since the last backup
         ContentResolver cr = mContext.getContentResolver();
         Cursor cursor = cr.query(WorkspaceScreens.CONTENT_URI, SCREEN_PROJECTION,
                 null, null, null);
-        Set<String> currentIds = new HashSet<String>(cursor.getCount());
         try {
             cursor.moveToPosition(-1);
-            if (DEBUG) Log.d(TAG, "dumping screens after: " + in.t);
+            if (DEBUG) Log.d(TAG, "dumping screens after: " + mLastBackupRestoreTime);
             while(cursor.moveToNext()) {
                 final long id = cursor.getLong(ID_INDEX);
                 final long updateTime = cursor.getLong(ID_MODIFIED);
                 Key key = getKey(Key.SCREEN, id);
-                keys.add(key);
+                mKeys.add(key);
                 final String backupKey = keyToBackupKey(key);
-                currentIds.add(backupKey);
-                if (!savedIds.contains(backupKey) || updateTime >= in.t) {
-                    byte[] blob = packScreen(cursor);
-                    writeRowToBackup(key, blob, out, data);
+                if (!mExistingKeys.contains(backupKey) || updateTime >= mLastBackupRestoreTime) {
+                    writeRowToBackup(key, packScreen(cursor), data);
                 } else {
-                    if (VERBOSE) Log.v(TAG, "screen " + id + " was too old: " + updateTime);
+                    if (VERBOSE) Log.v(TAG, "screen already backup up " + id);
                 }
             }
         } finally {
             cursor.close();
         }
-        if (DEBUG) Log.d(TAG, "screen currentIds.size()=" + currentIds.size());
-
-        // these IDs must have been deleted
-        savedIds.removeAll(currentIds);
-        out.rows += removeDeletedKeysFromBackup(savedIds, data);
     }
 
     /**
@@ -415,40 +389,24 @@
      * @param key identifier for the row
      * @param buffer the serialized proto from the stream, may be larger than dataSize
      * @param dataSize the size of the proto from the stream
-     * @param keys keys to mark as clean in the notes for next backup
      */
-    private void restoreScreen(Key key, byte[] buffer, int dataSize, ArrayList<Key> keys) {
+    private void restoreScreen(Key key, byte[] buffer, int dataSize) throws IOException {
         if (VERBOSE) Log.v(TAG, "unpacking screen " + key.id);
         if (DEBUG) Log.d(TAG, "read (" + buffer.length + "): " +
                 Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP));
 
-        if (!mRestoreEnabled) {
-            if (VERBOSE) Log.v(TAG, "restore not enabled: skipping database mutation");
-            return;
-        }
-
-        try {
-            ContentResolver cr = mContext.getContentResolver();
-            ContentValues values = unpackScreen(buffer, 0, dataSize);
-            cr.insert(WorkspaceScreens.CONTENT_URI, values);
-
-        } catch (InvalidProtocolBufferNanoException e) {
-            Log.e(TAG, "failed to decode screen", e);
-        }
+        ContentResolver cr = mContext.getContentResolver();
+        ContentValues values = unpackScreen(buffer, dataSize);
+        cr.insert(WorkspaceScreens.CONTENT_URI, values);
     }
 
     /**
      * Write all the static icon resources we need to render placeholders
      * for a package that is not installed.
      *
-     * @param in notes from last backup
      * @param data output stream for key/value pairs
-     * @param out notes about this backup
-     * @param keys keys to mark as clean in the notes for next backup
-     * @throws IOException
      */
-    private void backupIcons(Journal in, BackupDataOutput data, Journal out,
-            ArrayList<Key> keys) throws IOException {
+    private void backupIcons(BackupDataOutput data) throws IOException {
         // persist icons that haven't been persisted yet
         if (!initializeIconCache()) {
             dataChanged(); // try again later
@@ -458,21 +416,14 @@
         final ContentResolver cr = mContext.getContentResolver();
         final int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
         final UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
-
-        // read the old ID set
-        Set<String> savedIds = getSavedIdsByType(Key.ICON, in);
-        if (DEBUG) Log.d(TAG, "icon savedIds.size()=" + savedIds.size());
+        int backupUpIconCount = 0;
 
         // Don't backup apps in other profiles for now.
-        int startRows = out.rows;
-        if (DEBUG) Log.d(TAG, "starting here: " + startRows);
-
         String where = "(" + Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION + " OR " +
                 Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_SHORTCUT + ") AND " +
                 getUserSelectionArg();
         Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
                 where, null, null);
-        Set<String> currentIds = new HashSet<String>(cursor.getCount());
         try {
             cursor.moveToPosition(-1);
             while(cursor.moveToNext()) {
@@ -486,27 +437,26 @@
                     if (cn != null) {
                         key = getKey(Key.ICON, cn.flattenToShortString());
                         backupKey = keyToBackupKey(key);
-                        currentIds.add(backupKey);
                     } else {
                         Log.w(TAG, "empty intent on application favorite: " + id);
                     }
-                    if (savedIds.contains(backupKey)) {
-                        if (VERBOSE) Log.v(TAG, "already saved icon " + backupKey);
+                    if (mExistingKeys.contains(backupKey)) {
+                        if (DEBUG) Log.d(TAG, "already saved icon " + backupKey);
 
                         // remember that we already backed this up previously
-                        keys.add(key);
+                        mKeys.add(key);
                     } else if (backupKey != null) {
-                        if (DEBUG) Log.d(TAG, "I can count this high: " + out.rows);
-                        if ((out.rows - startRows) < MAX_ICONS_PER_PASS) {
-                            if (VERBOSE) Log.v(TAG, "saving icon " + backupKey);
+                        if (DEBUG) Log.d(TAG, "I can count this high: " + backupUpIconCount);
+                        if (backupUpIconCount < MAX_ICONS_PER_PASS) {
+                            if (DEBUG) Log.d(TAG, "saving icon " + backupKey);
                             Bitmap icon = mIconCache.getIcon(intent, myUserHandle);
-                            keys.add(key);
                             if (icon != null && !mIconCache.isDefaultIcon(icon, myUserHandle)) {
-                                byte[] blob = packIcon(dpi, icon);
-                                writeRowToBackup(key, blob, out, data);
+                                writeRowToBackup(key, packIcon(dpi, icon), data);
+                                mKeys.add(key);
+                                backupUpIconCount ++;
                             }
                         } else {
-                            if (VERBOSE) Log.d(TAG, "deferring icon backup " + backupKey);
+                            if (VERBOSE) Log.v(TAG, "deferring icon backup " + backupKey);
                             // too many icons for this pass, request another.
                             dataChanged();
                         }
@@ -521,11 +471,6 @@
         } finally {
             cursor.close();
         }
-        if (DEBUG) Log.d(TAG, "icon currentIds.size()=" + currentIds.size());
-
-        // these IDs must have been deleted
-        savedIds.removeAll(currentIds);
-        out.rows += removeDeletedKeysFromBackup(savedIds, data);
     }
 
     /**
@@ -536,55 +481,32 @@
      * @param key identifier for the row
      * @param buffer the serialized proto from the stream, may be larger than dataSize
      * @param dataSize the size of the proto from the stream
-     * @param keys keys to mark as clean in the notes for next backup
      */
-    private void restoreIcon(Key key, byte[] buffer, int dataSize, ArrayList<Key> keys) {
+    private void restoreIcon(Key key, byte[] buffer, int dataSize) throws IOException {
         if (VERBOSE) Log.v(TAG, "unpacking icon " + key.id);
         if (DEBUG) Log.d(TAG, "read (" + buffer.length + "): " +
                 Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP));
 
-        try {
-            Resource res = unpackIcon(buffer, 0, dataSize);
-            if (DEBUG) {
-                Log.d(TAG, "unpacked " + res.dpi + " dpi icon");
-            }
-            if (DEBUG_PAYLOAD) {
-                Log.d(TAG, "read " +
-                        Base64.encodeToString(res.data, 0, res.data.length,
-                                Base64.NO_WRAP));
-            }
-            Bitmap icon = BitmapFactory.decodeByteArray(res.data, 0, res.data.length);
-            if (icon == null) {
-                Log.w(TAG, "failed to unpack icon for " + key.name);
-            }
-
-            if (!mRestoreEnabled) {
-                if (VERBOSE) {
-                    Log.v(TAG, "restore not enabled: skipping database mutation");
-                }
-                return;
-            } else {
-                if (VERBOSE) Log.v(TAG, "saving restored icon as: " + key.name);
-                IconCache.preloadIcon(mContext, ComponentName.unflattenFromString(key.name),
-                        icon, res.dpi);
-            }
-        } catch (IOException e) {
-            Log.d(TAG, "failed to save restored icon for: " + key.name, e);
+        Resource res = unpackProto(new Resource(), buffer, dataSize);
+        if (DEBUG) {
+            Log.d(TAG, "unpacked " + res.dpi + " dpi icon");
         }
+        Bitmap icon = BitmapFactory.decodeByteArray(res.data, 0, res.data.length);
+        if (icon == null) {
+            Log.w(TAG, "failed to unpack icon for " + key.name);
+        }
+        if (VERBOSE) Log.v(TAG, "saving restored icon as: " + key.name);
+        IconCache.preloadIcon(mContext, ComponentName.unflattenFromString(key.name), icon, res.dpi);
     }
 
     /**
      * Write all the static widget resources we need to render placeholders
      * for a package that is not installed.
      *
-     * @param in notes from last backup
      * @param data output stream for key/value pairs
-     * @param out notes about this backup
-     * @param keys keys to mark as clean in the notes for next backup
      * @throws IOException
      */
-    private void backupWidgets(Journal in, BackupDataOutput data, Journal out,
-            ArrayList<Key> keys) throws IOException {
+    private void backupWidgets(BackupDataOutput data) throws IOException {
         // persist static widget info that hasn't been persisted yet
         final LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
         if (appState == null || !initializeIconCache()) {
@@ -597,18 +519,12 @@
         final int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
         final DeviceProfile profile = appState.getDynamicGrid().getDeviceProfile();
         if (DEBUG) Log.d(TAG, "cellWidthPx: " + profile.cellWidthPx);
+        int backupWidgetCount = 0;
 
-        // read the old ID set
-        Set<String> savedIds = getSavedIdsByType(Key.WIDGET, in);
-        if (DEBUG) Log.d(TAG, "widgets savedIds.size()=" + savedIds.size());
-
-        int startRows = out.rows;
-        if (DEBUG) Log.d(TAG, "starting here: " + startRows);
         String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPWIDGET + " AND "
                 + getUserSelectionArg();
         Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
                 where, null, null);
-        Set<String> currentIds = new HashSet<String>(cursor.getCount());
         try {
             cursor.moveToPosition(-1);
             while(cursor.moveToNext()) {
@@ -622,27 +538,25 @@
                 if (provider != null) {
                     key = getKey(Key.WIDGET, providerName);
                     backupKey = keyToBackupKey(key);
-                    currentIds.add(backupKey);
                 } else {
                     Log.w(TAG, "empty intent on appwidget: " + id);
                 }
-                if (savedIds.contains(backupKey)) {
-                    if (VERBOSE) Log.v(TAG, "already saved widget " + backupKey);
+                if (mExistingKeys.contains(backupKey)) {
+                    if (DEBUG) Log.d(TAG, "already saved widget " + backupKey);
 
                     // remember that we already backed this up previously
-                    keys.add(key);
+                    mKeys.add(key);
                 } else if (backupKey != null) {
-                    if (DEBUG) Log.d(TAG, "I can count this high: " + out.rows);
-                    if ((out.rows - startRows) < MAX_WIDGETS_PER_PASS) {
-                        if (VERBOSE) Log.v(TAG, "saving widget " + backupKey);
+                    if (DEBUG) Log.d(TAG, "I can count this high: " + backupWidgetCount);
+                    if (backupWidgetCount < MAX_WIDGETS_PER_PASS) {
+                        if (DEBUG) Log.d(TAG, "saving widget " + backupKey);
                         previewLoader.setPreviewSize(spanX * profile.cellWidthPx,
                                 spanY * profile.cellHeightPx, widgetSpacingLayout);
-                        byte[] blob = packWidget(dpi, previewLoader, mIconCache, provider);
-                        keys.add(key);
-                        writeRowToBackup(key, blob, out, data);
-
+                        writeRowToBackup(key, packWidget(dpi, previewLoader, mIconCache, provider), data);
+                        mKeys.add(key);
+                        backupWidgetCount ++;
                     } else {
-                        if (VERBOSE) Log.d(TAG, "deferring widget backup " + backupKey);
+                        if (VERBOSE) Log.v(TAG, "deferring widget backup " + backupKey);
                         // too many widgets for this pass, request another.
                         dataChanged();
                     }
@@ -651,11 +565,6 @@
         } finally {
             cursor.close();
         }
-        if (DEBUG) Log.d(TAG, "widget currentIds.size()=" + currentIds.size());
-
-        // these IDs must have been deleted
-        savedIds.removeAll(currentIds);
-        out.rows += removeDeletedKeysFromBackup(savedIds, data);
     }
 
     /**
@@ -666,35 +575,25 @@
      * @param key identifier for the row
      * @param buffer the serialized proto from the stream, may be larger than dataSize
      * @param dataSize the size of the proto from the stream
-     * @param keys keys to mark as clean in the notes for next backup
      */
-    private void restoreWidget(Key key, byte[] buffer, int dataSize, ArrayList<Key> keys) {
+    private void restoreWidget(Key key, byte[] buffer, int dataSize) throws IOException {
         if (VERBOSE) Log.v(TAG, "unpacking widget " + key.id);
         if (DEBUG) Log.d(TAG, "read (" + buffer.length + "): " +
                 Base64.encodeToString(buffer, 0, dataSize, Base64.NO_WRAP));
-        try {
-            Widget widget = unpackWidget(buffer, 0, dataSize);
-            if (DEBUG) Log.d(TAG, "unpacked " + widget.provider);
-            if (widget.icon.data != null)  {
-                Bitmap icon = BitmapFactory
-                        .decodeByteArray(widget.icon.data, 0, widget.icon.data.length);
-                if (icon == null) {
-                    Log.w(TAG, "failed to unpack widget icon for " + key.name);
-                } else {
-                    IconCache.preloadIcon(mContext, ComponentName.unflattenFromString(widget.provider),
-                            icon, widget.icon.dpi);
-                }
-            }
-
-            if (!mRestoreEnabled) {
-                if (VERBOSE) Log.v(TAG, "restore not enabled: skipping database mutation");
-                return;
+        Widget widget = unpackProto(new Widget(), buffer, dataSize);
+        if (DEBUG) Log.d(TAG, "unpacked " + widget.provider);
+        if (widget.icon.data != null)  {
+            Bitmap icon = BitmapFactory
+                    .decodeByteArray(widget.icon.data, 0, widget.icon.data.length);
+            if (icon == null) {
+                Log.w(TAG, "failed to unpack widget icon for " + key.name);
             } else {
-                // future site of widget table mutation
+                IconCache.preloadIcon(mContext, ComponentName.unflattenFromString(widget.provider),
+                        icon, widget.icon.dpi);
             }
-        } catch (InvalidProtocolBufferNanoException e) {
-            Log.e(TAG, "failed to decode widget", e);
         }
+
+        // future site of widget table mutation
     }
 
     /** create a new key, with an integer ID.
@@ -744,30 +643,6 @@
         }
     }
 
-    private String getKeyName(Key key) {
-        if (TextUtils.isEmpty(key.name)) {
-            return Long.toString(key.id);
-        } else {
-            return key.name;
-        }
-
-    }
-
-    private String geKeyType(Key key) {
-        switch (key.type) {
-            case Key.FAVORITE:
-                return "favorite";
-            case Key.SCREEN:
-                return "screen";
-            case Key.ICON:
-                return "icon";
-            case Key.WIDGET:
-                return "widget";
-            default:
-                return "anonymous";
-        }
-    }
-
     /** Compute the checksum over the important bits of a key. */
     private long checkKey(Key key) {
         CRC32 checksum = new CRC32();
@@ -781,7 +656,7 @@
     }
 
     /** Serialize a Favorite for persistence, including a checksum wrapper. */
-    private byte[] packFavorite(Cursor c) {
+    private Favorite packFavorite(Cursor c) {
         Favorite favorite = new Favorite();
         favorite.id = c.getLong(ID_INDEX);
         favorite.screen = c.getInt(SCREEN_INDEX);
@@ -819,7 +694,7 @@
                 favorite.intent = intent.toUri(0);
             } catch (URISyntaxException e) {
                 Log.e(TAG, "Invalid intent", e);
-           }
+            }
         }
         favorite.itemType = c.getInt(ITEM_TYPE_INDEX);
         if (favorite.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
@@ -830,16 +705,13 @@
             }
         }
 
-        return writeCheckedBytes(favorite);
+        return favorite;
     }
 
     /** Deserialize a Favorite from persistence, after verifying checksum wrapper. */
-    private ContentValues unpackFavorite(byte[] buffer, int offset, int dataSize)
+    private ContentValues unpackFavorite(byte[] buffer, int dataSize)
             throws InvalidProtocolBufferNanoException {
-        Favorite favorite = new Favorite();
-        MessageNano.mergeFrom(favorite, readCheckedBytes(buffer, offset, dataSize));
-        if (VERBOSE) Log.v(TAG, "unpacked favorite " + favorite.itemType + ", " +
-                (TextUtils.isEmpty(favorite.title) ? favorite.id : favorite.title));
+        Favorite favorite = unpackProto(new Favorite(), buffer, dataSize);
         ContentValues values = new ContentValues();
         values.put(Favorites._ID, favorite.id);
         values.put(Favorites.SCREEN, favorite.screen);
@@ -889,20 +761,17 @@
     }
 
     /** Serialize a Screen for persistence, including a checksum wrapper. */
-    private byte[] packScreen(Cursor c) {
+    private Screen packScreen(Cursor c) {
         Screen screen = new Screen();
         screen.id = c.getLong(ID_INDEX);
         screen.rank = c.getInt(SCREEN_RANK_INDEX);
-
-        return writeCheckedBytes(screen);
+        return screen;
     }
 
     /** Deserialize a Screen from persistence, after verifying checksum wrapper. */
-    private ContentValues unpackScreen(byte[] buffer, int offset, int dataSize)
+    private ContentValues unpackScreen(byte[] buffer, int dataSize)
             throws InvalidProtocolBufferNanoException {
-        Screen screen = new Screen();
-        MessageNano.mergeFrom(screen, readCheckedBytes(buffer, offset, dataSize));
-        if (VERBOSE) Log.v(TAG, "unpacked screen " + screen.id + "/" + screen.rank);
+        Screen screen = unpackProto(new Screen(), buffer, dataSize);
         ContentValues values = new ContentValues();
         values.put(WorkspaceScreens._ID, screen.id);
         values.put(WorkspaceScreens.SCREEN_RANK, screen.rank);
@@ -910,27 +779,18 @@
     }
 
     /** Serialize an icon Resource for persistence, including a checksum wrapper. */
-    private byte[] packIcon(int dpi, Bitmap icon) {
+    private Resource packIcon(int dpi, Bitmap icon) {
         Resource res = new Resource();
         res.dpi = dpi;
         ByteArrayOutputStream os = new ByteArrayOutputStream();
         if (icon.compress(IMAGE_FORMAT, IMAGE_COMPRESSION_QUALITY, os)) {
             res.data = os.toByteArray();
         }
-        return writeCheckedBytes(res);
-    }
-
-    /** Deserialize an icon resource from persistence, after verifying checksum wrapper. */
-    private static Resource unpackIcon(byte[] buffer, int offset, int dataSize)
-            throws InvalidProtocolBufferNanoException {
-        Resource res = new Resource();
-        MessageNano.mergeFrom(res, readCheckedBytes(buffer, offset, dataSize));
-        if (VERBOSE) Log.v(TAG, "unpacked icon " + res.dpi + "/" + res.data.length);
         return res;
     }
 
     /** Serialize a widget for persistence, including a checksum wrapper. */
-    private byte[] packWidget(int dpi, WidgetPreviewLoader previewLoader, IconCache iconCache,
+    private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader, IconCache iconCache,
             ComponentName provider) {
         final AppWidgetProviderInfo info = findAppWidgetProviderInfo(provider);
         Widget widget = new Widget();
@@ -956,16 +816,17 @@
                 widget.preview.dpi = dpi;
             }
         }
-        return writeCheckedBytes(widget);
+        return widget;
     }
 
-    /** Deserialize a widget from persistence, after verifying checksum wrapper. */
-    private Widget unpackWidget(byte[] buffer, int offset, int dataSize)
+    /**
+     * Deserialize a proto after verifying checksum wrapper.
+     */
+    private <T extends MessageNano> T unpackProto(T proto, byte[] buffer, int dataSize)
             throws InvalidProtocolBufferNanoException {
-        Widget widget = new Widget();
-        MessageNano.mergeFrom(widget, readCheckedBytes(buffer, offset, dataSize));
-        if (VERBOSE) Log.v(TAG, "unpacked widget " + widget.provider);
-        return widget;
+        MessageNano.mergeFrom(proto, readCheckedBytes(buffer, dataSize));
+        if (DEBUG) Log.d(TAG, "unpacked proto " + proto);
+        return proto;
     }
 
     /**
@@ -1001,9 +862,6 @@
                         if (result > 0) {
                             availableBytes -= result;
                             bytesRead += result;
-                            if (DEBUG && (bytesRead % 100 == 0)) {
-                                Log.d(TAG, "read some bytes: " + bytesRead);
-                            }
                         } else {
                             Log.w(TAG, "unexpected end of file while reading journal.");
                             // stop reading and see what there is to parse
@@ -1016,7 +874,7 @@
 
                     // check the buffer to see if we have a valid journal
                     try {
-                        MessageNano.mergeFrom(journal, readCheckedBytes(buffer, 0, bytesRead));
+                        MessageNano.mergeFrom(journal, readCheckedBytes(buffer, bytesRead));
                         // if we are here, then we have read a valid, checksum-verified journal
                         valid = true;
                         availableBytes = 0;
@@ -1044,46 +902,18 @@
         return journal;
     }
 
-    private void writeRowToBackup(Key key, byte[] blob, Journal out,
+
+    private void writeRowToBackup(Key key, MessageNano proto, BackupDataOutput data)
+            throws IOException {
+        writeRowToBackup(keyToBackupKey(key), proto, data);
+    }
+
+    private void writeRowToBackup(String backupKey, MessageNano proto,
             BackupDataOutput data) throws IOException {
-        String backupKey = keyToBackupKey(key);
+        byte[] blob = writeCheckedBytes(proto);
         data.writeEntityHeader(backupKey, blob.length);
         data.writeEntityData(blob, blob.length);
-        out.rows++;
-        out.bytes += blob.length;
-        if (VERBOSE) Log.v(TAG, "saving " + geKeyType(key) + " " + backupKey + ": " +
-                getKeyName(key) + "/" + blob.length);
-        if(DEBUG_PAYLOAD) {
-            String encoded = Base64.encodeToString(blob, 0, blob.length, Base64.NO_WRAP);
-            final int chunkSize = 1024;
-            for (int offset = 0; offset < encoded.length(); offset += chunkSize) {
-                int end = offset + chunkSize;
-                end = Math.min(end, encoded.length());
-                Log.w(TAG, "wrote " + encoded.substring(offset, end));
-            }
-        }
-    }
-
-    private Set<String> getSavedIdsByType(int type, Journal in) {
-        Set<String> savedIds = new HashSet<String>();
-        for(int i = 0; i < in.key.length; i++) {
-            Key key = in.key[i];
-            if (key.type == type) {
-                savedIds.add(keyToBackupKey(key));
-            }
-        }
-        return savedIds;
-    }
-
-    private int removeDeletedKeysFromBackup(Set<String> deletedIds, BackupDataOutput data)
-            throws IOException {
-        int rows = 0;
-        for(String deleted: deletedIds) {
-            if (VERBOSE) Log.v(TAG, "dropping deleted item " + deleted);
-            data.writeEntityHeader(deleted, -1);
-            rows++;
-        }
-        return rows;
+        if (VERBOSE) Log.v(TAG, "Writing New entry " + backupKey);
     }
 
     /**
@@ -1119,10 +949,10 @@
     }
 
     /** Unwrap a proto message from a CheckedMessage, verifying the checksum. */
-    private static byte[] readCheckedBytes(byte[] buffer, int offset, int dataSize)
+    private static byte[] readCheckedBytes(byte[] buffer, int dataSize)
             throws InvalidProtocolBufferNanoException {
         CheckedMessage wrapper = new CheckedMessage();
-        MessageNano.mergeFrom(wrapper, buffer, offset, dataSize);
+        MessageNano.mergeFrom(wrapper, buffer, 0, dataSize);
         CRC32 checksum = new CRC32();
         checksum.update(wrapper.payload);
         if (wrapper.checksum != checksum.getValue()) {
@@ -1161,7 +991,9 @@
     }
 
 
-   // check if the launcher is in a state to support backup
+    /**
+     * @return true if the launcher is in a state to support backup
+     */
     private boolean launcherIsReady() {
         ContentResolver cr = mContext.getContentResolver();
         Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION, null, null, null);
@@ -1185,7 +1017,7 @@
                 .getSerialNumberForUser(UserHandleCompat.myUserHandle());
     }
 
-    private class KeyParsingException extends Throwable {
+    private class KeyParsingException extends IOException {
         private KeyParsingException(Throwable cause) {
             super(cause);
         }
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index e0cfa27..a1f4e0b 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -98,11 +98,11 @@
     /**
      * Handshake to establish an overlay relationship
      *
-     * @param overlayView Full screen overlay ViewGroup into which custom views can be placed.
+     * @param container Full screen overlay ViewGroup into which custom views can be placed.
      * @param callbacks A set of callbacks provided by Launcher in relation to the overlay
      * @return an interface used to make requests and notify the Launcher in relation to the overlay
      */
-    public Launcher.LauncherOverlay setLauncherOverlayView(ViewGroup overlayView,
+    public Launcher.LauncherOverlay setLauncherOverlayView(InsettableFrameLayout container,
             Launcher.LauncherOverlayCallbacks callbacks);
 
 }
diff --git a/src/com/android/launcher3/LauncherExtension.java b/src/com/android/launcher3/LauncherExtension.java
index 10bbd35..b264042 100644
--- a/src/com/android/launcher3/LauncherExtension.java
+++ b/src/com/android/launcher3/LauncherExtension.java
@@ -252,11 +252,11 @@
         }
 
         @Override
-        public LauncherOverlay setLauncherOverlayView(ViewGroup overlayView,
+        public LauncherOverlay setLauncherOverlayView(InsettableFrameLayout container,
                 LauncherOverlayCallbacks callbacks) {
 
             mLauncherOverlay.setOverlayCallbacks(callbacks);
-            mLauncherOverlay.setOverlayView(overlayView);
+            mLauncherOverlay.setOverlayContainer(container);
 
             return mLauncherOverlay;
         }
@@ -335,9 +335,9 @@
                 hideOverlayPanel();
             }
 
-            public void setOverlayView(ViewGroup overlayView) {
+            public void setOverlayContainer(InsettableFrameLayout container) {
                 mOverlayView = (ViewGroup) getLayoutInflater().inflate(
-                        R.layout.launcher_overlay_example, overlayView);
+                        R.layout.launcher_overlay_example, container);
                 mSearchOverlay = mOverlayView.findViewById(R.id.search_overlay);
                 mSearchBox = mOverlayView.findViewById(R.id.search_box);
             }
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 9150dc0..f2d005e 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -1480,9 +1480,13 @@
 
                                     // Canonicalize
                                     // the Play Store sets the package parameter, but Launcher
-                                    // does not, so we clear that out to keep them the same
+                                    // does not, so we clear that out to keep them the same.
+                                    // Also ignore intent flags for the purposes of deduping.
                                     intent.setPackage(null);
+                                    int flags = intent.getFlags();
+                                    intent.setFlags(0);
                                     final String key = intent.toUri(0);
+                                    intent.setFlags(flags);
                                     if (seenIntents.contains(key)) {
                                         Launcher.addDumpLog(TAG, "skipping duplicate", true);
                                         continue;
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
new file mode 100644
index 0000000..e8c11c4
--- /dev/null
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -0,0 +1,17 @@
+package com.android.launcher3;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+
+public class LauncherRootView extends InsettableFrameLayout {
+    public LauncherRootView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected boolean fitSystemWindows(Rect insets) {
+        setInsets(insets);
+        return true; // I'll take it from here
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 518ee97..66d4ac0 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -295,6 +295,7 @@
     boolean mScrollInteractionBegan;
     boolean mStartedSendingScrollEvents;
     boolean mShouldSendPageSettled;
+    int mLastOverlaySroll = 0;
 
     private final Runnable mBindPages = new Runnable() {
         @Override
@@ -1287,8 +1288,11 @@
         boolean shouldOverScroll = (amount <= 0 && (!hasCustomContent() || isRtl)) ||
                 (amount >= 0 && (!hasCustomContent() || !isRtl));
 
-        boolean shouldScrollOverlay = (amount <= 0 && mLauncherOverlay != null && !isRtl) ||
-                (amount >= 0 && mLauncherOverlay != null && isRtl);
+        boolean shouldScrollOverlay = mLauncherOverlay != null &&
+                ((amount <= 0 && !isRtl) || (amount >= 0 && isRtl));
+
+        boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlaySroll != 0 &&
+                ((amount >= 0 && !isRtl) || (amount <= 0 && isRtl));
 
         if (shouldScrollOverlay) {
             if (!mStartedSendingScrollEvents && mScrollInteractionBegan) {
@@ -1301,6 +1305,7 @@
 
             int progress = (int) Math.abs((f * 100));
 
+            mLastOverlaySroll = progress;
             mLauncherOverlay.onScrollChange(progress, isRtl);
         } else if (shouldOverScroll) {
             dampedOverScroll(amount);
@@ -1308,6 +1313,10 @@
         } else {
             mOverScrollEffect = 0;
         }
+
+        if (shouldZeroOverlay) {
+            mLauncherOverlay.onScrollChange(0, isRtl);
+        }
     }
 
     @Override