Merge "Merge branch \'ub-launcher3-burnaby-nyc\' into launcher3merge am: 8c340dff19 am: 8249b928b7" into nyc-mr1-dev-plus-aosp
am: 3a40216fa0

Change-Id: I9771f7311fd49a535e91fc559024c9e17cff8cbb
diff --git a/Android.mk b/Android.mk
index d3823c3..ad8ce02 100644
--- a/Android.mk
+++ b/Android.mk
@@ -25,13 +25,15 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     android-support-v4 \
-    android-support-v7-recyclerview
+    android-support-v7-recyclerview \
+    android-support-v7-palette
 
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-    $(call all-java-files-under, WallpaperPicker/src) \
+LOCAL_SRC_FILES := \
+    $(call all-java-files-under, src) \
+    $(call all-java-files-under, src_config) \
     $(call all-proto-files-under, protos)
 
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/WallpaperPicker/res \
+LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/res \
     frameworks/support/v7/recyclerview/res
 
@@ -41,17 +43,18 @@
 LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/
 LOCAL_AAPT_FLAGS := \
     --auto-add-overlay \
-    --extra-packages android.support.v7.recyclerview
+    --extra-packages android.support.v7.recyclerview \
 
 LOCAL_SDK_VERSION := current
+LOCAL_MIN_SDK_VERSION := 21
 LOCAL_PACKAGE_NAME := Launcher3
-LOCAL_PRIVILEGED_MODULE := true
-#LOCAL_CERTIFICATE := shared
-
 LOCAL_OVERRIDES_PACKAGES := Home Launcher2
 
-include $(BUILD_PACKAGE)
+LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest-common.xml
 
+LOCAL_JACK_COVERAGE_INCLUDE_FILTER := com.android.launcher3.*
+
+include $(BUILD_PACKAGE)
 
 #
 # Launcher proto buffer jar used for development
@@ -70,4 +73,5 @@
 
 include $(BUILD_HOST_JAVA_LIBRARY)
 
+# ==================================================
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
new file mode 100644
index 0000000..bbe1f4a
--- /dev/null
+++ b/AndroidManifest-common.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2016, 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.
+*/
+-->
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.launcher3">
+    <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
+
+    <!--
+    The manifest defines the common entries that should be present in any derivative of Launcher3.
+    The components should generally not require any changes.
+
+    Rest of the components are defined in AndroidManifest.xml which is merged with this manifest
+    at compile time. Note that the components defined in AndroidManifest.xml are also required,
+    with some minor changed based on the derivative app.
+    -->
+    <permission
+        android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="dangerous"
+        android:label="@string/permlab_install_shortcut"
+        android:description="@string/permdesc_install_shortcut" />
+
+    <uses-permission android:name="android.permission.CALL_PHONE" />
+    <uses-permission android:name="android.permission.SET_WALLPAPER" />
+    <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
+    <uses-permission android:name="android.permission.BIND_APPWIDGET" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+
+    <application
+        android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+        android:fullBackupOnly="true"
+        android:fullBackupContent="@xml/backupscheme"
+        android:hardwareAccelerated="true"
+        android:icon="@mipmap/ic_launcher_home"
+        android:label="@string/derived_app_name"
+        android:largeHeap="@bool/config_largeHeap"
+        android:restoreAnyVersion="true"
+        android:supportsRtl="true" >
+
+        <!-- Intent received used to install shortcuts from other applications -->
+        <receiver
+            android:name="com.android.launcher3.InstallShortcutReceiver"
+            android:permission="com.android.launcher.permission.INSTALL_SHORTCUT">
+            <intent-filter>
+                <action android:name="com.android.launcher.action.INSTALL_SHORTCUT" />
+            </intent-filter>
+        </receiver>
+
+        <!-- Intent received used to initialize a restored widget -->
+        <receiver android:name="com.android.launcher3.AppWidgetsRestoredReceiver" >
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED"/>
+            </intent-filter>
+        </receiver>
+
+        <service android:name="com.android.launcher3.dynamicui.ColorExtractionService"
+            android:exported="false"
+            android:process=":wallpaper_chooser">
+        </service>
+
+        <meta-data android:name="android.nfc.disable_beam_default"
+                       android:value="true" />
+
+    </application>
+</manifest>
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 918ae52..6c5990d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,14 +20,17 @@
 <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.launcher3">
-    <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="16"/>
+    <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
+    <!--
+    Manifest entries specific to Launcher3. This is merged with AndroidManifest-common.xml.
+    Refer comments around specific entries on how to extend individual components.
+    -->
 
-    <permission
-        android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
-        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:protectionLevel="dangerous"
-        android:label="@string/permlab_install_shortcut"
-        android:description="@string/permdesc_install_shortcut" />
+    <!--
+    Permissions required for read/write access to the workspace data. These permission name
+    should not conflict with that defined in other apps, as such an app should embed its package
+    name in the permissions. eq com.mypackage.permission.READ_SETTINGS
+    -->
     <permission
         android:name="com.android.launcher3.permission.READ_SETTINGS"
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
@@ -40,45 +43,33 @@
         android:protectionLevel="signatureOrSystem"
         android:label="@string/permlab_write_settings"
         android:description="@string/permdesc_write_settings"/>
-    <permission
-        android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS"
-        android:protectionLevel="signature"
-        />
-    <permission
-        android:name="com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST"
-        android:protectionLevel="signatureOrSystem" />
 
-    <uses-permission android:name="android.permission.CALL_PHONE" />
-    <uses-permission android:name="android.permission.SET_WALLPAPER" />
-    <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
-    <uses-permission android:name="android.permission.VIBRATE" />
-    <uses-permission android:name="android.permission.BIND_APPWIDGET" />
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.BROADCAST_STICKY"/>
-    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
     <uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
     <uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
     <uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />
-    <uses-permission android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS" />
-    <uses-permission android:name="com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST" />
 
     <application
-        android:allowBackup="@bool/enable_backup"
-        android:backupAgent="com.android.launcher3.LauncherBackupAgentHelper"
+        android:backupAgent="com.android.launcher3.LauncherBackupAgent"
+        android:fullBackupOnly="true"
+        android:fullBackupContent="@xml/backupscheme"
         android:hardwareAccelerated="true"
         android:icon="@mipmap/ic_launcher_home"
-        android:label="@string/app_name"
+        android:label="@string/derived_app_name"
         android:largeHeap="@bool/config_largeHeap"
         android:restoreAnyVersion="true"
         android:supportsRtl="true" >
 
+        <!--
+        Main launcher activity. When extending only change the name, and keep all the
+        attributes and intent filters the same
+        -->
         <activity
             android:name="com.android.launcher3.Launcher"
             android:launchMode="singleTask"
             android:clearTaskOnLaunch="true"
             android:stateNotNeeded="true"
-            android:theme="@style/Theme"
+            android:theme="@style/LauncherTheme"
             android:windowSoftInputMode="adjustPan"
             android:screenOrientation="nosensor"
             android:configChanges="keyboard|keyboardHidden|navigation"
@@ -93,63 +84,24 @@
             </intent-filter>
         </activity>
 
-        <activity
-            android:name="com.android.launcher3.WallpaperPickerActivity"
-            android:theme="@style/Theme.WallpaperPicker"
-            android:label="@string/pick_wallpaper"
-            android:icon="@mipmap/ic_launcher_wallpaper"
-            android:finishOnCloseSystemDialogs="true"
-            android:process=":wallpaper_chooser">
-            <intent-filter>
-                <action android:name="android.intent.action.SET_WALLPAPER" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-
-        <activity
-            android:name="com.android.launcher3.WallpaperCropActivity"
-            android:theme="@style/Theme.WallpaperCropper"
-            android:label="@string/crop_wallpaper"
-            android:icon="@mipmap/ic_launcher_wallpaper"
-            android:finishOnCloseSystemDialogs="true"
-            android:process=":wallpaper_chooser">
-            <intent-filter>
-                <action android:name="android.service.wallpaper.CROP_AND_SET_WALLPAPER" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:mimeType="image/*" />
-            </intent-filter>
-        </activity>
-
+        <!--
+        The settings activity. When extending keep the intent filter present
+        -->
         <activity
             android:name="com.android.launcher3.SettingsActivity"
             android:label="@string/settings_button_text"
-            android:autoRemoveFromRecents="true"
-            android:process=":settings_process">
+            android:autoRemoveFromRecents="true">
+            <intent-filter>
+                <action android:name="android.intent.action.APPLICATION_PREFERENCES" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
         </activity>
 
-        <!-- Intent received used to install shortcuts from other applications -->
-        <receiver
-            android:name="com.android.launcher3.InstallShortcutReceiver"
-            android:permission="com.android.launcher.permission.INSTALL_SHORTCUT">
-            <intent-filter>
-                <action android:name="com.android.launcher.action.INSTALL_SHORTCUT" />
-            </intent-filter>
-        </receiver>
-
-        <!-- Intent received used to initialize a restored widget -->
-        <receiver android:name="com.android.launcher3.AppWidgetsRestoredReceiver" >
-            <intent-filter>
-                <action android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED"/>
-            </intent-filter>
-        </receiver>
-
-        <receiver android:name="com.android.launcher3.StartupReceiver" >
-            <intent-filter>
-                <action android:name="android.intent.action.BOOT_COMPLETED" />
-            </intent-filter>
-        </receiver>
-
-        <!-- The settings provider contains Home's data, like the workspace favorites -->
+        <!--
+        The settings provider contains Home's data, like the workspace favorites. The permissions
+        should be changed to what is defined above. The authorities should also be changed to
+        represent the package name.
+        -->
         <provider
             android:name="com.android.launcher3.LauncherProvider"
             android:authorities="com.android.launcher3.settings"
@@ -157,9 +109,6 @@
             android:writePermission="com.android.launcher3.permission.WRITE_SETTINGS"
             android:readPermission="com.android.launcher3.permission.READ_SETTINGS" />
 
-        <meta-data android:name="android.nfc.disable_beam_default"
-                       android:value="true" />
-
         <!-- ENABLE_FOR_TESTING
 
         <activity
diff --git a/WallpaperPicker/AndroidManifest.xml b/WallpaperPicker/AndroidManifest.xml
deleted file mode 100644
index cb1457b..0000000
--- a/WallpaperPicker/AndroidManifest.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.launcher3"
-        android:versionCode="1"
-        android:versionName="1.0"
-        >
-
-    <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="21" />
-    <application/>
-</manifest>
diff --git a/WallpaperPicker/README b/WallpaperPicker/README
deleted file mode 100644
index d8efb07..0000000
--- a/WallpaperPicker/README
+++ /dev/null
@@ -1,4 +0,0 @@
-This project contains the wallpaper picker for Launcher3. It's in a separate
-folder to organize the code separately from the rest of the launcher, and has
-a manifest so that a separate Eclipse project can exist for the wallpaper
-picker (necessary to have the Eclipse build work)
\ No newline at end of file
diff --git a/WallpaperPicker/res/anim/fade_out.xml b/WallpaperPicker/res/anim/fade_out.xml
deleted file mode 100644
index 2749d92..0000000
--- a/WallpaperPicker/res/anim/fade_out.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<!-- startOffset is the same as the duration of the wallpaper_enter animation. We have this delay so
-    that we don't see the wallpaper changing before fading back to the home screen. -->
-<alpha xmlns:android="http://schemas.android.com/apk/res/android"
-    android:startOffset="@android:integer/config_longAnimTime"
-    android:duration="@android:integer/config_mediumAnimTime"
-    android:fromAlpha="1"
-    android:toAlpha="0"/>
-
diff --git a/WallpaperPicker/res/drawable-hdpi/ic_actionbar_accept.png b/WallpaperPicker/res/drawable-hdpi/ic_actionbar_accept.png
deleted file mode 100755
index 53cf687..0000000
--- a/WallpaperPicker/res/drawable-hdpi/ic_actionbar_accept.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-hdpi/ic_images.png b/WallpaperPicker/res/drawable-hdpi/ic_images.png
deleted file mode 100644
index 15e511c..0000000
--- a/WallpaperPicker/res/drawable-hdpi/ic_images.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-hdpi/tile_shadow_bottom.9.png b/WallpaperPicker/res/drawable-hdpi/tile_shadow_bottom.9.png
deleted file mode 100644
index e80558b..0000000
--- a/WallpaperPicker/res/drawable-hdpi/tile_shadow_bottom.9.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-hdpi/tile_shadow_top.9.png b/WallpaperPicker/res/drawable-hdpi/tile_shadow_top.9.png
deleted file mode 100644
index 7e93865..0000000
--- a/WallpaperPicker/res/drawable-hdpi/tile_shadow_top.9.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-mdpi/ic_actionbar_accept.png b/WallpaperPicker/res/drawable-mdpi/ic_actionbar_accept.png
deleted file mode 100755
index 35cda8e..0000000
--- a/WallpaperPicker/res/drawable-mdpi/ic_actionbar_accept.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-mdpi/ic_images.png b/WallpaperPicker/res/drawable-mdpi/ic_images.png
deleted file mode 100644
index c4a2229..0000000
--- a/WallpaperPicker/res/drawable-mdpi/ic_images.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-mdpi/tile_shadow_bottom.9.png b/WallpaperPicker/res/drawable-mdpi/tile_shadow_bottom.9.png
deleted file mode 100644
index d95787b..0000000
--- a/WallpaperPicker/res/drawable-mdpi/tile_shadow_bottom.9.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-mdpi/tile_shadow_top.9.png b/WallpaperPicker/res/drawable-mdpi/tile_shadow_top.9.png
deleted file mode 100644
index 8da913c..0000000
--- a/WallpaperPicker/res/drawable-mdpi/tile_shadow_top.9.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-v21/wallpaper_tile_fg.xml b/WallpaperPicker/res/drawable-v21/wallpaper_tile_fg.xml
deleted file mode 100644
index 97cdcd6..0000000
--- a/WallpaperPicker/res/drawable-v21/wallpaper_tile_fg.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 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.
--->
-
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="#66FFFFFF" >
-
-    <item
-        android:id="@android:id/mask"
-        android:drawable="@android:color/white"/>
-    <item
-        android:bottom="23.25dp"
-        android:left="29.25dp"
-        android:right="29.25dp"
-        android:top="23.25dp">
-        <selector>
-            <item
-                android:drawable="@drawable/ic_tick"
-                android:state_selected="true"/>
-            <item
-                android:drawable="@drawable/ic_tick"
-                android:state_checked="true"/>
-        </selector>
-    </item>
-
-</ripple>
\ No newline at end of file
diff --git a/WallpaperPicker/res/drawable-xhdpi/ic_actionbar_accept.png b/WallpaperPicker/res/drawable-xhdpi/ic_actionbar_accept.png
deleted file mode 100755
index b52dc37..0000000
--- a/WallpaperPicker/res/drawable-xhdpi/ic_actionbar_accept.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-xhdpi/ic_images.png b/WallpaperPicker/res/drawable-xhdpi/ic_images.png
deleted file mode 100644
index 4974792..0000000
--- a/WallpaperPicker/res/drawable-xhdpi/ic_images.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-xhdpi/tile_shadow_bottom.9.png b/WallpaperPicker/res/drawable-xhdpi/tile_shadow_bottom.9.png
deleted file mode 100644
index 81571f3..0000000
--- a/WallpaperPicker/res/drawable-xhdpi/tile_shadow_bottom.9.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-xhdpi/tile_shadow_top.9.png b/WallpaperPicker/res/drawable-xhdpi/tile_shadow_top.9.png
deleted file mode 100644
index 8503a59..0000000
--- a/WallpaperPicker/res/drawable-xhdpi/tile_shadow_top.9.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-xxhdpi/ic_actionbar_accept.png b/WallpaperPicker/res/drawable-xxhdpi/ic_actionbar_accept.png
deleted file mode 100755
index d9ad51c..0000000
--- a/WallpaperPicker/res/drawable-xxhdpi/ic_actionbar_accept.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-xxhdpi/ic_images.png b/WallpaperPicker/res/drawable-xxhdpi/ic_images.png
deleted file mode 100644
index c8b9f75..0000000
--- a/WallpaperPicker/res/drawable-xxhdpi/ic_images.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-xxhdpi/tile_shadow_bottom.9.png b/WallpaperPicker/res/drawable-xxhdpi/tile_shadow_bottom.9.png
deleted file mode 100644
index 55250f0..0000000
--- a/WallpaperPicker/res/drawable-xxhdpi/tile_shadow_bottom.9.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-xxhdpi/tile_shadow_top.9.png b/WallpaperPicker/res/drawable-xxhdpi/tile_shadow_top.9.png
deleted file mode 100644
index 3f22633..0000000
--- a/WallpaperPicker/res/drawable-xxhdpi/tile_shadow_top.9.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable-xxxhdpi/ic_images.png b/WallpaperPicker/res/drawable-xxxhdpi/ic_images.png
deleted file mode 100644
index a19002e..0000000
--- a/WallpaperPicker/res/drawable-xxxhdpi/ic_images.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/drawable/wallpaper_tile_fg.xml b/WallpaperPicker/res/drawable/wallpaper_tile_fg.xml
deleted file mode 100644
index c66fa50..0000000
--- a/WallpaperPicker/res/drawable/wallpaper_tile_fg.xml
+++ /dev/null
@@ -1,47 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_checked="true" >
-        <shape>
-            <stroke
-                android:width="2dp"
-                android:color="#FFFFFFFF" />
-            <solid android:color="#33FFFFFF"/>
-        </shape>
-    </item>
-    <item android:state_focused="true" >
-        <shape>
-            <stroke
-                android:width="2dp"
-                android:color="#FFFFFFFF" />
-        </shape>
-    </item>
-    <item android:state_pressed="true">
-        <shape android:shape="rectangle">
-            <solid android:color="#33FFFFFF"/>
-        </shape>
-    </item>
-    <item android:state_selected="true" >
-        <shape>
-            <stroke
-                android:width="2dp"
-                android:color="#FFFFFFFF" />
-            <solid android:color="#33FFFFFF"/>
-        </shape>
-    </item>
-    <item android:drawable="@android:color/transparent" />
-</selector>
diff --git a/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml b/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml
deleted file mode 100644
index 2b39b09..0000000
--- a/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<com.android.launcher3.AlphaDisableableButton
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@style/ActionBarSetWallpaperStyle"
-    android:id="@+id/set_wallpaper_button"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:paddingRight="20dp"
-    android:drawableLeft="@drawable/ic_actionbar_accept"
-    android:drawablePadding="8dp"
-    android:gravity="start|center_vertical"
-    android:text="@string/wallpaper_instructions"
-    android:enabled="false">
-</com.android.launcher3.AlphaDisableableButton>
diff --git a/WallpaperPicker/res/layout/wallpaper_cropper.xml b/WallpaperPicker/res/layout/wallpaper_cropper.xml
deleted file mode 100644
index ffe8df0..0000000
--- a/WallpaperPicker/res/layout/wallpaper_cropper.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <com.android.launcher3.CropView
-        android:id="@+id/cropView"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-    <ProgressBar
-        android:id="@+id/loading"
-        style="?android:attr/progressBarStyleLarge"
-        android:visibility="invisible"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_centerInParent="true"
-        android:indeterminate="true"
-        android:indeterminateOnly="true"
-        android:background="@android:color/transparent" />
-</RelativeLayout>
diff --git a/WallpaperPicker/res/layout/wallpaper_picker.xml b/WallpaperPicker/res/layout/wallpaper_picker.xml
deleted file mode 100644
index 0b970b0..0000000
--- a/WallpaperPicker/res/layout/wallpaper_picker.xml
+++ /dev/null
@@ -1,91 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" >
-
-    <com.android.launcher3.CropView
-        android:id="@+id/cropView"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-
-    <ProgressBar
-        android:id="@+id/loading"
-        style="?android:attr/progressBarStyleLarge"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:indeterminate="true"
-        android:indeterminateOnly="true"
-        android:visibility="invisible" />
-
-    <LinearLayout
-        android:id="@+id/wallpaper_strip"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="bottom"
-        android:fitsSystemWindows="true"
-        android:orientation="vertical" >
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="2dp"
-            android:background="@drawable/tile_shadow_top" />
-
-        <HorizontalScrollView
-            android:id="@+id/wallpaper_scroll_container"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" >
-
-            <LinearLayout
-                android:id="@+id/master_wallpaper_list"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:orientation="horizontal" >
-
-                <LinearLayout
-                    android:id="@+id/wallpaper_list"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:orientation="horizontal" />
-
-                <LinearLayout
-                    android:id="@+id/live_wallpaper_list"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:orientation="horizontal" />
-
-                <LinearLayout
-                    android:id="@+id/third_party_wallpaper_list"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:orientation="horizontal" />
-            </LinearLayout>
-        </HorizontalScrollView>
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="2dp"
-            android:background="@drawable/tile_shadow_bottom" />
-    </LinearLayout>
-
-</FrameLayout>
\ No newline at end of file
diff --git a/WallpaperPicker/res/layout/wallpaper_picker_image_picker_item.xml b/WallpaperPicker/res/layout/wallpaper_picker_image_picker_item.xml
deleted file mode 100644
index dc65244..0000000
--- a/WallpaperPicker/res/layout/wallpaper_picker_image_picker_item.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<com.android.launcher3.CheckableFrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/wallpaperThumbnailWidth"
-    android:layout_height="@dimen/wallpaperThumbnailHeight"
-    android:focusable="true"
-    android:clickable="true"
-    android:foreground="@drawable/wallpaper_tile_fg">
-    <ImageView
-        android:id="@+id/wallpaper_image"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@color/wallpaper_picker_translucent_gray"
-        android:scaleType="centerCrop" />
-    <TextView
-        android:id="@+id/wallpaper_item_label"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:ellipsize="marquee"
-        android:gravity="center"
-        android:layout_gravity="center"
-        android:text="@string/pick_image"
-        android:drawableTop="@drawable/ic_images"
-        android:drawablePadding="4dp"
-        android:textColor="@android:color/white"/>
-</com.android.launcher3.CheckableFrameLayout>
diff --git a/WallpaperPicker/res/layout/wallpaper_picker_item.xml b/WallpaperPicker/res/layout/wallpaper_picker_item.xml
deleted file mode 100644
index 3f57fcd..0000000
--- a/WallpaperPicker/res/layout/wallpaper_picker_item.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<com.android.launcher3.CheckableFrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/wallpaperThumbnailWidth"
-    android:layout_height="@dimen/wallpaperThumbnailHeight"
-    android:focusable="true"
-    android:clickable="true"
-    android:foreground="@drawable/wallpaper_tile_fg">
-    <ImageView
-        android:id="@+id/wallpaper_image"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:scaleType="centerCrop" />
-</com.android.launcher3.CheckableFrameLayout>
diff --git a/WallpaperPicker/res/layout/wallpaper_picker_live_wallpaper_item.xml b/WallpaperPicker/res/layout/wallpaper_picker_live_wallpaper_item.xml
deleted file mode 100644
index 2b152fc..0000000
--- a/WallpaperPicker/res/layout/wallpaper_picker_live_wallpaper_item.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<com.android.launcher3.CheckableFrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/wallpaperThumbnailWidth"
-    android:layout_height="@dimen/wallpaperThumbnailHeight"
-    android:focusable="true"
-    android:clickable="true"
-    android:foreground="@drawable/wallpaper_tile_fg">
-    <ImageView
-        android:id="@+id/wallpaper_image"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_gravity="center"
-        android:background="@android:color/black"
-        android:scaleType="centerCrop" />
-    <ImageView
-        android:id="@+id/wallpaper_icon"
-        android:layout_width="@dimen/wallpaperItemIconSize"
-        android:layout_height="@dimen/wallpaperItemIconSize"
-        android:layout_gravity="center"
-        android:visibility="gone" />
-    <TextView
-        android:id="@+id/wallpaper_item_label"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:singleLine="true"
-        android:ellipsize="marquee"
-        android:gravity="center"
-        android:padding="4dp"
-        android:layout_gravity="bottom"
-        android:background="@color/wallpaper_picker_translucent_gray"
-        android:textColor="@android:color/white"/>
-</com.android.launcher3.CheckableFrameLayout>
diff --git a/WallpaperPicker/res/layout/wallpaper_picker_third_party_item.xml b/WallpaperPicker/res/layout/wallpaper_picker_third_party_item.xml
deleted file mode 100644
index a7e3a0c..0000000
--- a/WallpaperPicker/res/layout/wallpaper_picker_third_party_item.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2008 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<com.android.launcher3.CheckableFrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/wallpaperThumbnailWidth"
-    android:layout_height="@dimen/wallpaperThumbnailHeight"
-    android:focusable="true"
-    android:clickable="true"
-    android:foreground="@drawable/wallpaper_tile_fg">
-    <ImageView
-        android:id="@+id/wallpaper_image"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@color/wallpaper_picker_translucent_gray"
-        android:scaleType="centerCrop" />
-    <TextView
-        android:id="@+id/wallpaper_item_label"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:ellipsize="marquee"
-        android:gravity="center"
-        android:layout_gravity="center"
-        android:drawablePadding="4dp"
-        android:textColor="@android:color/white"/>
-</com.android.launcher3.CheckableFrameLayout>
diff --git a/WallpaperPicker/res/menu/cab_delete_wallpapers.xml b/WallpaperPicker/res/menu/cab_delete_wallpapers.xml
deleted file mode 100644
index 38ac5c4..0000000
--- a/WallpaperPicker/res/menu/cab_delete_wallpapers.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android" >
-  <item
-     android:id="@+id/menu_delete"
-     android:title="@string/wallpaper_delete"
-     android:showAsAction="always"
-     android:icon="@android:drawable/ic_menu_delete" />
-</menu>
diff --git a/WallpaperPicker/res/mipmap-hdpi/ic_launcher_wallpaper.png b/WallpaperPicker/res/mipmap-hdpi/ic_launcher_wallpaper.png
deleted file mode 100644
index affee85..0000000
--- a/WallpaperPicker/res/mipmap-hdpi/ic_launcher_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/mipmap-mdpi/ic_launcher_wallpaper.png b/WallpaperPicker/res/mipmap-mdpi/ic_launcher_wallpaper.png
deleted file mode 100644
index cb4443b..0000000
--- a/WallpaperPicker/res/mipmap-mdpi/ic_launcher_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/mipmap-xhdpi/ic_launcher_wallpaper.png b/WallpaperPicker/res/mipmap-xhdpi/ic_launcher_wallpaper.png
deleted file mode 100644
index 60f8dce..0000000
--- a/WallpaperPicker/res/mipmap-xhdpi/ic_launcher_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/mipmap-xxhdpi/ic_launcher_wallpaper.png b/WallpaperPicker/res/mipmap-xxhdpi/ic_launcher_wallpaper.png
deleted file mode 100644
index 023fb58..0000000
--- a/WallpaperPicker/res/mipmap-xxhdpi/ic_launcher_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/WallpaperPicker/res/values-af/strings.xml b/WallpaperPicker/res/values-af/strings.xml
deleted file mode 100644
index c926f48..0000000
--- a/WallpaperPicker/res/values-af/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Stel muurpapier"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Kon nie prent laai nie"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Kon nie prent as muurpapier laai nie"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kon nie prent as muurpapier stel nie"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d gekies</item>
-      <item quantity="one">%1$d gekies</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Muurpapier %1$d van %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Het <xliff:g id="LABEL">%1$s</xliff:g> gekies"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Vee uit"</string>
-    <string name="pick_image" msgid="3189640419551368385">"My foto\'s"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Muurpapiere"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Snoei muurpapier"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-am/strings.xml b/WallpaperPicker/res/values-am/strings.xml
deleted file mode 100644
index 1b067d4..0000000
--- a/WallpaperPicker/res/values-am/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"ልጣፍ  አዘጋጅ"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"ምስሉን መጫን አልተቻለም"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"ምስሉን እንደ ግድግዳ ወረቀት መጫን አልተቻለም"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"ምስሉን እንደ ግድግዳ ወረቀት ማዘጋጀት አልተቻለም"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d ተመርጧል</item>
-      <item quantity="other">%1$d ተመርጠዋል</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"ልጣፍ  %1$d የ%2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> ተመርጧል"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"ሰርዝ"</string>
-    <string name="pick_image" msgid="3189640419551368385">"የእኔ ፎቶዎች"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"የግድግዳ ወረቀቶች"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"ልጣፍ  ይከርክሙ"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-ar/strings.xml b/WallpaperPicker/res/values-ar/strings.xml
deleted file mode 100644
index 354b344..0000000
--- a/WallpaperPicker/res/values-ar/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"تعيين الخلفية"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"تعذر تحميل الصورة"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"تعذر تحميل الصورة كخلفية"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"تعذر تعيين الصورة كخلفية"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="zero">‏تم تحديد %1$d</item>
-      <item quantity="two">‏تم تحديد %1$d</item>
-      <item quantity="few">‏تم تحديد %1$d</item>
-      <item quantity="many">‏تم تحديد %1$d</item>
-      <item quantity="other">‏تم تحديد %1$d</item>
-      <item quantity="one">‏تم تحديد %1$d</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"‏الخلفية %1$d من %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"تم تحديد <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"حذف"</string>
-    <string name="pick_image" msgid="3189640419551368385">"صوري"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"الخلفيات"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"اقتصاص الخلفية"</string>
-    <string name="which_wallpaper_option_home_screen" msgid="5259213374485080595">"الشاشة الرئيسية"</string>
-    <string name="which_wallpaper_option_lock_screen" msgid="5474588303389139825">"شاشة التأمين"</string>
-    <string name="which_wallpaper_option_home_screen_and_lock_screen" msgid="7652312651094808607">"الشاشة الرئيسية وشاشة التأمين"</string>
-</resources>
diff --git a/WallpaperPicker/res/values-az-rAZ/strings.xml b/WallpaperPicker/res/values-az-rAZ/strings.xml
deleted file mode 100644
index cb894c9..0000000
--- a/WallpaperPicker/res/values-az-rAZ/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Divar kağı seçin"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Şəkli yükləmək alınmadı"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Şəkli divar kağızı olaraq yükləmək alınmadı"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Şəkli divar kağızı olaraq quraşdırmaq alınmadı"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d seçilib</item>
-      <item quantity="one">%1$d seçilib</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Divar kağızı %1$d of %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> seçilib"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Sil"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Fotolarım"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Divar kağızları"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Divar kağızını kəsin"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-b+sr+Latn/strings.xml b/WallpaperPicker/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index 2ac0803..0000000
--- a/WallpaperPicker/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Podesi pozadinu"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Nije moguće učitati sliku"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Nije moguće učitati sliku kao pozadinu"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Učitavanje slike kao pozadine nije uspelo"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">Izabrana je %1$d stavka</item>
-      <item quantity="few">Izabrane su %1$d stavke</item>
-      <item quantity="other">Izabrano je %1$d stavki</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Pozadina %1$d od %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Izabrana je <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Izbriši"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Moje fotografije"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Pozadine"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Opseci pozadinu"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-be-rBY/strings.xml b/WallpaperPicker/res/values-be-rBY/strings.xml
deleted file mode 100644
index c78814a..0000000
--- a/WallpaperPicker/res/values-be-rBY/strings.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Задаць шпалеры"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Не атрымалася загрузіць відарыс"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Не атрымалася загрузіць відарыс у якасці шпалер"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Не атрымалася задаць відарыс як шпалеры"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d выбраны</item>
-      <item quantity="few">%1$d выбраны</item>
-      <item quantity="many">%1$d выбраны</item>
-      <item quantity="other">%1$d выбраны</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Шпалеры %1$d з %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Выбрана: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Выдаліць"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Мае фатаграфii"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Шпалеры"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Абрэзаць шпалеры"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-bg/strings.xml b/WallpaperPicker/res/values-bg/strings.xml
deleted file mode 100644
index bd49629..0000000
--- a/WallpaperPicker/res/values-bg/strings.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Задаване на тапета"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Изображението не можа да бъде заредено"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Изображението не можа да бъде заредено като тапет"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Изображението не можа да бъде зададено като тапет"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">Избрахте %1$d</item>
-      <item quantity="one">Избрахте %1$d</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Тапет %1$d от %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Избрахте <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Изтриване"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Моите снимки"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Тапети"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Подрязване на тапета"</string>
-    <string name="which_wallpaper_option_home_screen" msgid="5259213374485080595">"Начален екран"</string>
-    <string name="which_wallpaper_option_lock_screen" msgid="5474588303389139825">"Заключен екран"</string>
-    <string name="which_wallpaper_option_home_screen_and_lock_screen" msgid="7652312651094808607">"Начален и заключен екран"</string>
-</resources>
diff --git a/WallpaperPicker/res/values-bn-rBD/strings.xml b/WallpaperPicker/res/values-bn-rBD/strings.xml
deleted file mode 100644
index 79a370e..0000000
--- a/WallpaperPicker/res/values-bn-rBD/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"ওয়ালপেপার সেট করুন"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"চিত্র লোড করা যায়নি"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"ওয়ালপেপার হিসাবে চিত্র লোড করা যায়নি"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"ওয়ালপেপার হিসাবে চিত্র সেট করা যায়নি"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$dটি নির্বাচন করা হয়েছে</item>
-      <item quantity="other">%1$dটি নির্বাচন করা হয়েছে</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$dটির মধ্যে %1$dটি ওয়ালপেপার"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> নির্বাচন করা হয়েছে"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"মুছুন"</string>
-    <string name="pick_image" msgid="3189640419551368385">"আমার ফটো"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"ওয়ালপেপারগুলি"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"ওয়ালপেপার কাটছাঁট করুন"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-bs-rBA/strings.xml b/WallpaperPicker/res/values-bs-rBA/strings.xml
deleted file mode 100644
index 304bc57..0000000
--- a/WallpaperPicker/res/values-bs-rBA/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Postavi pozadinu"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Nije moguće učitati sliku"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Nije moguće učitati sliku kao pozadinu"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nije moguće postaviti sliku kao pozadinu"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">odabran je %1$d</item>
-      <item quantity="few">odabrana su %1$d</item>
-      <item quantity="other">odabrano je %1$d</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Pozadina %1$d od %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Odabrano: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Izbriši"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Moje fotografije"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Pozadine"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Izreži pozadinsku sliku"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-ca/strings.xml b/WallpaperPicker/res/values-ca/strings.xml
deleted file mode 100644
index 3110e34..0000000
--- a/WallpaperPicker/res/values-ca/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Estableix el fons de pantalla"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"No s\'ha pogut carregar la imatge."</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"No s\'ha pogut carregar la imatge com a fons de pantalla."</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"No s\'ha pogut definir la imatge com a fons de pantalla"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">Seleccionats: %1$d</item>
-      <item quantity="one">Seleccionats: %1$d</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Fons de pantalla %1$d de %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"S\'ha seleccionat <xliff:g id="LABEL">%1$s</xliff:g>."</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Suprimeix"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Les meves fotos"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Fons de pantalla"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Retallar fons de pantalla"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-cs/strings.xml b/WallpaperPicker/res/values-cs/strings.xml
deleted file mode 100644
index 8ec211a..0000000
--- a/WallpaperPicker/res/values-cs/strings.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Nastavit jako tapetu"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Obrázek nelze načíst."</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Obrázek nelze načíst jako tapetu."</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Obrázek nelze nastavit jako tapetu"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="few">Vybráno: %1$d</item>
-      <item quantity="many">Vybráno: %1$d</item>
-      <item quantity="other">Vybráno: %1$d</item>
-      <item quantity="one">Vybráno: %1$d</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Tapeta %1$d z %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Vybrána položka <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Smazat"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Moje fotografie"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Tapety"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Oříznutí tapety"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-da/strings.xml b/WallpaperPicker/res/values-da/strings.xml
deleted file mode 100644
index 58409b8..0000000
--- a/WallpaperPicker/res/values-da/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Angiv baggrund"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Billedet kunne ikke indlæses"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Billedet kunne ikke indlæses som baggrund"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Billedet kunne ikke indlæses som baggrund"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d er valgt</item>
-      <item quantity="other">%1$d er valgt</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Baggrund %1$d af %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> blev valgt"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Slet"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Mine billeder"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Baggrunde"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Beskær baggrunden"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-de/strings.xml b/WallpaperPicker/res/values-de/strings.xml
deleted file mode 100644
index 175e6a7..0000000
--- a/WallpaperPicker/res/values-de/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Hintergrund auswählen"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Bild konnte nicht geladen werden."</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Bild konnte nicht als Hintergrund geladen werden."</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Bild konnte nicht als Hintergrund festgelegt werden."</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d ausgewählt</item>
-      <item quantity="one">%1$d ausgewählt</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Hintergrund %1$d von %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> ausgewählt"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Löschen"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Meine Fotos"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Hintergründe"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Hintergrund zuschneiden"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-el/strings.xml b/WallpaperPicker/res/values-el/strings.xml
deleted file mode 100644
index 4b2963e..0000000
--- a/WallpaperPicker/res/values-el/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Ορισμός ταπετσαρίας"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Δεν ήταν δυνατή η φόρτωση της εικόνας"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Δεν ήταν δυνατή η φόρτωση της εικόνας ως ταπετσαρία"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Δεν ήταν δυνατός ο ορισμός της εικόνας ως ταπετσαρία"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d επιλεγμένες</item>
-      <item quantity="one">%1$d επιλεγμένη</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Ταπετσαρία %1$d από %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Επιλέχθηκε το <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Διαγραφή"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Οι φωτογραφίες μου"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Ταπετσαρίες"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Περικοπή ταπετσαρίας"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-en-rAU/strings.xml b/WallpaperPicker/res/values-en-rAU/strings.xml
deleted file mode 100644
index d9b9cb1..0000000
--- a/WallpaperPicker/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Set wallpaper"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Couldn\'t load image"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Couldn\'t load image as wallpaper"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Couldn\'t set image as wallpaper"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d selected</item>
-      <item quantity="one">%1$d selected</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Wallpaper %1$d of %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Selected <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Delete"</string>
-    <string name="pick_image" msgid="3189640419551368385">"My photos"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Wallpapers"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Crop wallpaper"</string>
-    <string name="which_wallpaper_option_home_screen" msgid="5259213374485080595">"Home screen"</string>
-    <string name="which_wallpaper_option_lock_screen" msgid="5474588303389139825">"Lock screen"</string>
-    <string name="which_wallpaper_option_home_screen_and_lock_screen" msgid="7652312651094808607">"Home screen and lock screen"</string>
-</resources>
diff --git a/WallpaperPicker/res/values-en-rGB/strings.xml b/WallpaperPicker/res/values-en-rGB/strings.xml
deleted file mode 100644
index d9b9cb1..0000000
--- a/WallpaperPicker/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Set wallpaper"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Couldn\'t load image"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Couldn\'t load image as wallpaper"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Couldn\'t set image as wallpaper"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d selected</item>
-      <item quantity="one">%1$d selected</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Wallpaper %1$d of %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Selected <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Delete"</string>
-    <string name="pick_image" msgid="3189640419551368385">"My photos"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Wallpapers"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Crop wallpaper"</string>
-    <string name="which_wallpaper_option_home_screen" msgid="5259213374485080595">"Home screen"</string>
-    <string name="which_wallpaper_option_lock_screen" msgid="5474588303389139825">"Lock screen"</string>
-    <string name="which_wallpaper_option_home_screen_and_lock_screen" msgid="7652312651094808607">"Home screen and lock screen"</string>
-</resources>
diff --git a/WallpaperPicker/res/values-en-rIN/strings.xml b/WallpaperPicker/res/values-en-rIN/strings.xml
deleted file mode 100644
index d9b9cb1..0000000
--- a/WallpaperPicker/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Set wallpaper"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Couldn\'t load image"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Couldn\'t load image as wallpaper"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Couldn\'t set image as wallpaper"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d selected</item>
-      <item quantity="one">%1$d selected</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Wallpaper %1$d of %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Selected <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Delete"</string>
-    <string name="pick_image" msgid="3189640419551368385">"My photos"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Wallpapers"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Crop wallpaper"</string>
-    <string name="which_wallpaper_option_home_screen" msgid="5259213374485080595">"Home screen"</string>
-    <string name="which_wallpaper_option_lock_screen" msgid="5474588303389139825">"Lock screen"</string>
-    <string name="which_wallpaper_option_home_screen_and_lock_screen" msgid="7652312651094808607">"Home screen and lock screen"</string>
-</resources>
diff --git a/WallpaperPicker/res/values-es-rUS/strings.xml b/WallpaperPicker/res/values-es-rUS/strings.xml
deleted file mode 100644
index cb62513..0000000
--- a/WallpaperPicker/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Establecer como fondo de pantalla"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"No se pudo cargar la imagen."</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"No se pudo cargar la imagen como fondo de pantalla."</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"No se pudo establecer la imagen como fondo de pantalla"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d seleccionados</item>
-      <item quantity="one">%1$d seleccionado</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Fondo de pantalla %1$d de %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> seleccionado"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Eliminar"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Mis fotos"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Fondos de pantalla"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Recortar fondo de pantalla"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-es/strings.xml b/WallpaperPicker/res/values-es/strings.xml
deleted file mode 100644
index 121b392..0000000
--- a/WallpaperPicker/res/values-es/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Establecer fondo"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"No se ha podido cargar la imagen"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"No se ha podido cargar la imagen como fondo de pantalla"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"No se ha podido establecer la imagen como fondo de pantalla"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d seleccionados</item>
-      <item quantity="one">%1$d seleccionado</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Fondo de pantalla %1$d de %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> seleccionado"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Eliminar"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Mis fotos"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Fondos de pantalla"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Recortar fondo de pantalla"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-et-rEE/strings.xml b/WallpaperPicker/res/values-et-rEE/strings.xml
deleted file mode 100644
index 53be957..0000000
--- a/WallpaperPicker/res/values-et-rEE/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Määra taustapilt"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Kujutist ei õnnestunud laadida"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Kujutist ei õnnestunud taustapildina laadida"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kujutist ei õnnestunud taustapildiks määrata"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">Valitud on %1$d</item>
-      <item quantity="one">Valitud on %1$d</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d/%2$d taustapildist"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Valitud on <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Kustuta"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Minu fotod"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Taustapildid"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Taustapildi kärpimine"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-eu-rES/strings.xml b/WallpaperPicker/res/values-eu-rES/strings.xml
deleted file mode 100644
index 8eb490c..0000000
--- a/WallpaperPicker/res/values-eu-rES/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Ezarri horma-papera"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Ezin izan da irudia kargatu"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Ezin izan da irudia horma-paper gisa kargatu"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Ezin izan da ezarri irudia horma-paper gisa"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d hautatu dira</item>
-      <item quantity="one">%1$d hautatu da</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d/%2$d horma-papera"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> hautatu da"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Ezabatu"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Nire argazkiak"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Horma-paperak"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Ebaki horma-papera"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-fa/strings.xml b/WallpaperPicker/res/values-fa/strings.xml
deleted file mode 100644
index 9764e9a..0000000
--- a/WallpaperPicker/res/values-fa/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"تنظیم کاغذدیواری"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"تصویر بارگیری نشد"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"تصویر به‌عنوان کاغذدیواری بارگیری نشد"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"تصویر به‌عنوان کاغذدیواری تنظیم نشد"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">‏%1$d انتخاب شد</item>
-      <item quantity="other">‏%1$d انتخاب شد</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"‏کاغذدیواری %1$d از %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> انتخاب شد"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"حذف"</string>
-    <string name="pick_image" msgid="3189640419551368385">"عکس‌های من"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"کاغذدیواری‌ها"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"برش کاغذدیواری"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-fi/strings.xml b/WallpaperPicker/res/values-fi/strings.xml
deleted file mode 100644
index 7d5a30a..0000000
--- a/WallpaperPicker/res/values-fi/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Aseta taustakuva"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Kuvan lataus epäonnistui"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Kuvaa ei voitu ladata taustakuvaksi"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kuvan asettaminen taustakuvaksi epäonnistui."</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d valittu</item>
-      <item quantity="one">%1$d valittu</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Taustakuva %1$d/%2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Valittu: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Poista"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Omat valokuvat"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Taustakuvat"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Rajaa taustakuva"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-fr-rCA/strings.xml b/WallpaperPicker/res/values-fr-rCA/strings.xml
deleted file mode 100644
index 78ed165..0000000
--- a/WallpaperPicker/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Définir le fond d\'écran"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Impossible de charger l\'image"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Impossible de charger l\'image comme fond d\'écran"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Impossible d\'utiliser l\'image comme fond d\'écran"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d fond d\'écran sélectionné</item>
-      <item quantity="other">%1$d fonds d\'écran sélectionnés</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Fond d\'écran %1$d de %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Sélection : <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Supprimer"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Mes photos"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Fonds d\'écran"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Rogner le fond d\'écran"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-fr/strings.xml b/WallpaperPicker/res/values-fr/strings.xml
deleted file mode 100644
index 8a3ba67..0000000
--- a/WallpaperPicker/res/values-fr/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Définir comme fond d\'écran"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Impossible de charger l\'image."</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Impossible de charger l\'image comme fond d\'écran."</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Impossible de définir l\'image comme fond d\'écran."</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d fond d\'écran sélectionné</item>
-      <item quantity="other">%1$d fonds d\'écran sélectionnés</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Fond d\'écran %1$d sur %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> sélectionné"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Supprimer"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Mes photos"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Fonds d\'écran"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Recadrer le fond d\'écran"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-gl-rES/strings.xml b/WallpaperPicker/res/values-gl-rES/strings.xml
deleted file mode 100644
index 4ac52c6..0000000
--- a/WallpaperPicker/res/values-gl-rES/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Establecer fondo de pantalla"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Non se puido cargar a imaxe"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Non se puido cargar a imaxe como fondo de pantalla"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Non se puido definir a imaxe como fondo de pantalla"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">Seleccionáronse %1$d elementos</item>
-      <item quantity="one">Seleccionouse %1$d elemento</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Fondo de pantalla %1$d de %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Seleccionaches <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Eliminar"</string>
-    <string name="pick_image" msgid="3189640419551368385">"As miñas fotos"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Fondos de pantalla"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Recortar fondo de pantalla"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-gu-rIN/strings.xml b/WallpaperPicker/res/values-gu-rIN/strings.xml
deleted file mode 100644
index 48fc6e8..0000000
--- a/WallpaperPicker/res/values-gu-rIN/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"વૉલપેપર સેટ કરો"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"છબી લોડ કરી શકાઈ નથી"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"વૉલપેપર તરીકે છબી લોડ કરી શકાઈ નથી"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"વૉલપેપર તરીકે છબી સેટ કરી શક્યાં નથી"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d પસંદ કર્યાં</item>
-      <item quantity="other">%1$d પસંદ કર્યાં</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$d માંથી %1$d વૉલપેપર"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> પસંદ કર્યો"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"કાઢી નાખો"</string>
-    <string name="pick_image" msgid="3189640419551368385">"મારા ફોટા"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"વૉલપેપર્સ"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"વૉલપેપર કાપો"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-hi/strings.xml b/WallpaperPicker/res/values-hi/strings.xml
deleted file mode 100644
index 5b2cd62..0000000
--- a/WallpaperPicker/res/values-hi/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"वॉलपेपर सेट करें"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"चित्र लोड नहीं किया जा सका"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"चित्र को वॉलपेपर के रूप में लोड नहीं किया जा सका"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"चित्र को वॉलपेपर के रूप में सेट नहीं किया जा सका"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d चुना गया</item>
-      <item quantity="other">%1$d चुने गए</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"वॉलपेपर %2$d में से %1$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"चयनित <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"हटाएं"</string>
-    <string name="pick_image" msgid="3189640419551368385">"मेरी फ़ोटो"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"वॉलपेपर"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"वॉलपेपर काटें"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-hr/strings.xml b/WallpaperPicker/res/values-hr/strings.xml
deleted file mode 100644
index fa98cfa..0000000
--- a/WallpaperPicker/res/values-hr/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Postavi pozadinu"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Nije moguće učitati sliku"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Nije moguće učitati sliku kao pozadinu"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Postavljanje slike kao pozadine nije uspjelo"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">Odabrana je %1$d stavka</item>
-      <item quantity="few">Odabrane su %1$d stavke</item>
-      <item quantity="other">Odabrano je %1$d stavki</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d. pozadinska slika od %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Odabrana je <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Izbriši"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Moje fotografije"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Pozadine"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Obrezivanje pozadinske slike"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-hu/strings.xml b/WallpaperPicker/res/values-hu/strings.xml
deleted file mode 100644
index bb59ee3..0000000
--- a/WallpaperPicker/res/values-hu/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Háttérkép beállítása"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"A kép betöltése nem sikerült"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"A kép betöltése háttérképként nem sikerült"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"A kép beállítása háttérképként nem sikerült"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d kiválasztva</item>
-      <item quantity="one">%1$d kiválasztva</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d/%2$d. háttérkép"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> kiválasztva"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Törlés"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Saját fotók"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Háttérképek"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Háttérkép körbevágása"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-hy-rAM/strings.xml b/WallpaperPicker/res/values-hy-rAM/strings.xml
deleted file mode 100644
index 8d011c3..0000000
--- a/WallpaperPicker/res/values-hy-rAM/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Սահմանել պաստառը"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Չհաջողվեց բեռնել նկարը"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Չհաջողվեց նկարը սահմանել որպես պաստառ"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Չհաջողվեց նկարը դնել որպես պաստառ"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d ընտրված</item>
-      <item quantity="other">%1$d ընտրված</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d պաստառ՝ %2$d-ից"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Ընտրված է <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Ջնջել"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Իմ լուսանկարները"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Պաստառներ"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Եզրատել պաստառը"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-in/strings.xml b/WallpaperPicker/res/values-in/strings.xml
deleted file mode 100644
index 990a235..0000000
--- a/WallpaperPicker/res/values-in/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Setel wallpaper"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Tidak dapat memuat gambar"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Tidak dapat memuat gambar sebagai wallpaper"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Tidak dapat menyetel gambar sebagai wallpaper"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d dipilih</item>
-      <item quantity="one">%1$d dipilih</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Wallpaper %1$d dari %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> terpilih"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Hapus"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Foto saya"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Wallpaper"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Pangkas wallpaper"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-is-rIS/strings.xml b/WallpaperPicker/res/values-is-rIS/strings.xml
deleted file mode 100644
index 9c1db9f..0000000
--- a/WallpaperPicker/res/values-is-rIS/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Velja veggfóður"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Ekki var hægt að hlaða mynd"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Ekki var hægt að hlaða mynd sem veggfóður"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Ekki var hægt að nota mynd sem veggfóður"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d valið</item>
-      <item quantity="other">%1$d valin</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Veggfóður %1$d af %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> valið"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Eyða"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Myndirnar mínar"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Veggfóður"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Skera veggfóður"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-it/strings.xml b/WallpaperPicker/res/values-it/strings.xml
deleted file mode 100644
index 91430dd..0000000
--- a/WallpaperPicker/res/values-it/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Imposta sfondo"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Impossibile caricare l\'immagine"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Impossibile caricare l\'immagine come sfondo"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Impossibile impostare l\'immagine come sfondo"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d selezionati</item>
-      <item quantity="one">%1$d selezionato</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Sfondo %1$d di %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Elemento selezionato: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Elimina"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Le mie foto"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Sfondi"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Ritaglia sfondo"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-iw/strings.xml b/WallpaperPicker/res/values-iw/strings.xml
deleted file mode 100644
index 72ad4c2..0000000
--- a/WallpaperPicker/res/values-iw/strings.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"הגדר טפט"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"לא ניתן היה לטעון את התמונה"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"לא ניתן היה לטעון את התמונה כטפט"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"לא ניתן היה להגדיר את התמונה כטפט"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="two">‏נבחרו %1$d פריטים</item>
-      <item quantity="many">‏נבחרו %1$d פריטים</item>
-      <item quantity="other">‏נבחרו %1$d פריטים</item>
-      <item quantity="one">נבחר פריט אחד</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"‏טפט %1$d מתוך %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"בחרת <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"מחק"</string>
-    <string name="pick_image" msgid="3189640419551368385">"התמונות שלי"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"טפטים"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"חתוך את הטפט"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-ja/strings.xml b/WallpaperPicker/res/values-ja/strings.xml
deleted file mode 100644
index 39e8270..0000000
--- a/WallpaperPicker/res/values-ja/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"壁紙を設定"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"画像を読み込めませんでした"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"画像を壁紙として読み込めませんでした"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"画像を壁紙として設定できませんでした"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d 個を選択済み</item>
-      <item quantity="one">%1$d 個を選択済み</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"壁紙: %1$d/%2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"選択: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"削除"</string>
-    <string name="pick_image" msgid="3189640419551368385">"マイフォト"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"壁紙"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"壁紙のトリミング"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-ka-rGE/strings.xml b/WallpaperPicker/res/values-ka-rGE/strings.xml
deleted file mode 100644
index 1cc78cd..0000000
--- a/WallpaperPicker/res/values-ka-rGE/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"ფონის დაყენება"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"სურათი ვერ ჩაიტვირთა."</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"სურათი ფონად ვერ ჩაიტვირთა."</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"სურათი ფონად ვერ დაყენდა"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">არჩეულია %1$d</item>
-      <item quantity="one">არჩეულია %1$d</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"ფონი %1$d %2$d-დან"</string>
-    <string name="announce_selection" msgid="123723511662250539">"არჩეული <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"წაშლა"</string>
-    <string name="pick_image" msgid="3189640419551368385">"ჩემი ფოტოები"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"ფონები"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"ფონის ჩამოჭრა"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-kk-rKZ/strings.xml b/WallpaperPicker/res/values-kk-rKZ/strings.xml
deleted file mode 100644
index c78a8d8..0000000
--- a/WallpaperPicker/res/values-kk-rKZ/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Тұсқағаз орнату"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Суретті жүктей алмады"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Суретті артқы фон ретінде жүктей алмады"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Кескінді тұсқағаз ретінде орнату мүмкін болмады"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d таңдалған</item>
-      <item quantity="one">%1$d таңдалған</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d артқы фон, барлығы %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> таңдалған"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Жою"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Менің фотосуреттерім"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Артқы фондар"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Артқы фонды кесу"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-km-rKH/strings.xml b/WallpaperPicker/res/values-km-rKH/strings.xml
deleted file mode 100644
index 402f28e..0000000
--- a/WallpaperPicker/res/values-km-rKH/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"កំណត់​ផ្ទាំង​រូបភាព"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"មិន​អាច​ផ្ទុក​រូបភាព"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"មិន​អាច​ផ្ទុក​រូបភាព​ជា​ផ្ទាំង​រូបភាព"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"មិនអាចកំណត់រូបភាពជាផ្ទាំងរូបភាពទេ"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">បានជ្រើស %1$d</item>
-      <item quantity="one">បានជ្រើស %1$d</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"ផ្ទាំង​រូបភាព %1$d នៃ %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"បាន​ជ្រើស <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"លុប"</string>
-    <string name="pick_image" msgid="3189640419551368385">"រូបថតរបស់ខ្ញុំ"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"ផ្ទាំង​រូបភាព"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"ច្រឹប​ផ្ទាំង​រូបភាព"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-kn-rIN/strings.xml b/WallpaperPicker/res/values-kn-rIN/strings.xml
deleted file mode 100644
index 1b467f4..0000000
--- a/WallpaperPicker/res/values-kn-rIN/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"ವಾಲ್‌ಪೇಪರ್ ಹೊಂದಿಸಿ"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"ಚಿತ್ರವನ್ನು ಲೋಡ್‌ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"ಚಿತ್ರವನ್ನು ವಾಲ್‌ಪೇಪರ್‌ ರೂಪದಲ್ಲಿ ಲೋಡ್‌ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"ಚಿತ್ರವನ್ನು ವಾಲ್‌ಪೇಪರ್‌ ರೂಪದಲ್ಲಿ ಹೊಂದಿಸಲಾಗಲಿಲ್ಲ"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ</item>
-      <item quantity="other">%1$d ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$d ರಲ್ಲಿ %1$d ವಾಲ್‌ಪೇಪರ್‌"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"ಅಳಿಸು"</string>
-    <string name="pick_image" msgid="3189640419551368385">"ನನ್ನ ಫೋಟೋಗಳು"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"ವಾಲ್‌ಪೇಪರ್‌ಗಳು"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"ವಾಲ್‌ಪೇಪರ್‌ ಕತ್ತರಿಸಿ"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-ko/strings.xml b/WallpaperPicker/res/values-ko/strings.xml
deleted file mode 100644
index 7da91da..0000000
--- a/WallpaperPicker/res/values-ko/strings.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"배경화면 설정"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"이미지를 로드할 수 없습니다."</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"이미지를 배경화면으로 로드할 수 없습니다."</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"이미지를 배경화면으로 설정할 수 없습니다."</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d개 선택됨</item>
-      <item quantity="one">%1$d개 선택됨</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"배경화면 %1$d/%2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> 선택함"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"삭제"</string>
-    <string name="pick_image" msgid="3189640419551368385">"내 사진"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"배경화면"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"배경화면 잘라내기"</string>
-    <string name="which_wallpaper_option_home_screen" msgid="5259213374485080595">"메인 스크린"</string>
-    <string name="which_wallpaper_option_lock_screen" msgid="5474588303389139825">"잠금 화면"</string>
-    <string name="which_wallpaper_option_home_screen_and_lock_screen" msgid="7652312651094808607">"메인 스크린 및 잠금 화면"</string>
-</resources>
diff --git a/WallpaperPicker/res/values-ky-rKG/strings.xml b/WallpaperPicker/res/values-ky-rKG/strings.xml
deleted file mode 100644
index acf8b5d..0000000
--- a/WallpaperPicker/res/values-ky-rKG/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Тушкагаз орнотуу"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Сүрөт жүктөө мүмкүн болбоду"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Сүрөттү тушкагаз катары жүктөө кыйрады"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Сүрөт тушкагаз катары коюлбай койду"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d тандалды</item>
-      <item quantity="one">%1$d тандалды</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$d ичинен %1$d тушкагаз"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> тандалды"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Жок кылуу"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Менин сүрөттөрүм"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Тушкагаздар"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Тушкагазды тегиздөө"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-lo-rLA/strings.xml b/WallpaperPicker/res/values-lo-rLA/strings.xml
deleted file mode 100644
index f6bfa1e..0000000
--- a/WallpaperPicker/res/values-lo-rLA/strings.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"ຕັ້ງເປັນພາບພື້ນຫຼັງ"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"ບໍ່ສາມາດໂຫຼດຮູບໄດ້"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"ບໍ່ສາມາດໂຫຼດຮູບເປັນພາບພື້ນຫຼັງໄດ້"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"ບໍ່ສາມາດໂຫຼດຮູບເປັນພາບພື້ນຫຼັງໄດ້"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">ເລືອກ %1$d ແລ້ວ</item>
-      <item quantity="one">ເລືອກ %1$d ແລ້ວ</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"ພາບພື້ນຫຼັງ %1$d ໃນ %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"ເລືອກ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"ລຶບ"</string>
-    <string name="pick_image" msgid="3189640419551368385">"ຮູບຂອງຂ້ອຍ"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"ພາບພື້ນຫຼັງ"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"ຕັດພາບພື້ນຫຼັງ"</string>
-    <string name="which_wallpaper_option_home_screen" msgid="5259213374485080595">"ໜ້າຈໍຫຼັກ"</string>
-    <string name="which_wallpaper_option_lock_screen" msgid="5474588303389139825">"ໜ້າຈໍລັອກ"</string>
-    <string name="which_wallpaper_option_home_screen_and_lock_screen" msgid="7652312651094808607">"ໜ້າຈໍຫຼັກ ແລະ ໜ້າຈໍລັອກ"</string>
-</resources>
diff --git a/WallpaperPicker/res/values-lt/strings.xml b/WallpaperPicker/res/values-lt/strings.xml
deleted file mode 100644
index f9127d8..0000000
--- a/WallpaperPicker/res/values-lt/strings.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Nustatyti ekrano foną"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Nepavyko įkelti vaizdo"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Nepavyko įkelti vaizdo kaip ekrano fono"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nepavyko nustatyti vaizdo kaip ekrano fono"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">Pasirinktas %1$d elementas</item>
-      <item quantity="few">Pasirinkti %1$d elementai</item>
-      <item quantity="many">Pasirinkta %1$d elemento</item>
-      <item quantity="other">Pasirinkta %1$d elementų</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d ekrano fonas iš %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Pasirinkta: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Ištrinti"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Mano nuotraukos"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Ekrano fonai"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Apkirpti ekrano foną"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-lv/strings.xml b/WallpaperPicker/res/values-lv/strings.xml
deleted file mode 100644
index d8601dc..0000000
--- a/WallpaperPicker/res/values-lv/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Iestatīt fona tapeti"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Nevarēja ielādēt attēlu."</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Nevarēja ielādēt attēlu kā fona tapeti."</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nevarēja iestatīt attēlu kā fona tapeti."</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="zero">Atlasītas: %1$d</item>
-      <item quantity="one">Atlasītas: %1$d</item>
-      <item quantity="other">Atlasītas: %1$d</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d. fona tapete no %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Atlasīta: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Dzēst"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Mani fotoattēli"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Fona tapetes"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Apgriezt fona tapeti"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-mk-rMK/strings.xml b/WallpaperPicker/res/values-mk-rMK/strings.xml
deleted file mode 100644
index c881595..0000000
--- a/WallpaperPicker/res/values-mk-rMK/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Подеси тапет"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Сликата не можеше да се вчита"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Сликата не можеше да се вчита како тапет"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Сликата не може да се постави како тапет"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">Избран е %1$d</item>
-      <item quantity="other">Избрани се %1$d</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Тапет %1$d од %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Избран <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Избриши"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Моите фотографии"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Тапети"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Исечи тапет"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-ml-rIN/strings.xml b/WallpaperPicker/res/values-ml-rIN/strings.xml
deleted file mode 100644
index 35eb6c0..0000000
--- a/WallpaperPicker/res/values-ml-rIN/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"വാൾപേപ്പർ സജ്ജീകരിക്കുക"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"ചിത്രം ലോഡുചെയ്യാനായില്ല"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"വാൾപേപ്പറായി ചിത്രം ലോഡുചെയ്യാനായില്ല"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"വാൾപേപ്പറായി ചിത്രം ലോഡുചെയ്യാനായില്ല"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d തിരഞ്ഞെടുത്തു</item>
-      <item quantity="one">%1$d തിരഞ്ഞെടുത്തു</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d / %2$d വാൾപേപ്പർ"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> തിരഞ്ഞെടുത്തു"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"ഇല്ലാതാക്കുക"</string>
-    <string name="pick_image" msgid="3189640419551368385">"എന്റെ ഫോട്ടോകൾ"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"വാൾപേപ്പറുകൾ"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"വാൾപേപ്പറിന്റെ വലുപ്പം മാറ്റൽ"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-mn-rMN/strings.xml b/WallpaperPicker/res/values-mn-rMN/strings.xml
deleted file mode 100644
index ca55055..0000000
--- a/WallpaperPicker/res/values-mn-rMN/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Ханын зургийг тохируулах"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Зургийг ачаалж чадсангүй"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Зургийг ханын зураг болгож ачаалж чадсангүй"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Зургийг ханын зураг болгож чадсангүй"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d-г сонгосон</item>
-      <item quantity="one">%1$d-г сонгосон</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$d ханын цаасны %1$d нь"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> сонгогдсон"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Устгах"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Миний зураг"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Ханын зураг"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Ханын зургийг тайрах"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-mr-rIN/strings.xml b/WallpaperPicker/res/values-mr-rIN/strings.xml
deleted file mode 100644
index 3d76d20..0000000
--- a/WallpaperPicker/res/values-mr-rIN/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"वॉलपेपर सेट करा"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"प्रतिमा लोड करू शकलो नाही"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"वॉलपेपर म्हणून प्रतिमा लोड करू शकलो नाही"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"प्रतिमा वॉलपेपर म्हणून सेट करू शकलो नाही"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d निवडला</item>
-      <item quantity="other">%1$d निवडले</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$d पैकी %1$d वॉलपेपर"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> निवडले"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"हटवा"</string>
-    <string name="pick_image" msgid="3189640419551368385">"माझे फोटो"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"वॉलपेपर"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"वॉलपेपर कापा"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-ms-rMY/strings.xml b/WallpaperPicker/res/values-ms-rMY/strings.xml
deleted file mode 100644
index a60ba06..0000000
--- a/WallpaperPicker/res/values-ms-rMY/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Tetapkan kertas dinding"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Tidak dapat memuatkan imej"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Tidak dapat memuatkan imej sebagai kertas dinding"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Tidak dapat menetapkan imej sebagai kertas dinding"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d dipilih</item>
-      <item quantity="one">%1$d dipilih</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Kertas dinding %1$d daripada %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Memilih <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Padam"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Foto saya"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Kertas dinding"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Pangkas kertas dinding"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-my-rMM/strings.xml b/WallpaperPicker/res/values-my-rMM/strings.xml
deleted file mode 100644
index 32d92fa..0000000
--- a/WallpaperPicker/res/values-my-rMM/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"နောက်ခံအား သတ်မှတ်ရန်"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"ပုံရိပ် တင် မရပါ"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"ပုံရိပ်အား နောက်ခံအဖြစ် တင် မရပါ"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"ပုံရိပ်အား နောက်ခံအဖြစ် တင်၍မရပါ"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d ကို ရွေးချယ်ထားပါသည်</item>
-      <item quantity="one">%1$d ကို ရွေးချယ်ထားပါသည်</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"နောက်ခံ %1$d မှ %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"ရွေးချယ်ထားသော <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"ဖျက်ပါ"</string>
-    <string name="pick_image" msgid="3189640419551368385">"ကျွန်ုပ်၏ ဓာတ်ပုံများ"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"နောက်ခံများ"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"နောက်ခံအား ဖြတ်ခြင်း"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-nb/strings.xml b/WallpaperPicker/res/values-nb/strings.xml
deleted file mode 100644
index d9ce246..0000000
--- a/WallpaperPicker/res/values-nb/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Angi bakgrunn"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Kunne ikke laste inn bildet"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Kunne ikke laste inn bildet som bakgrunn"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kunne ikke angi bildet som bakgrunn"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d er valgt</item>
-      <item quantity="one">%1$d er valgt</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Bakgrunn %1$d av %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Valgt <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Slett"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Mine bilder"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Bakgrunner"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Beskjær bakgrunnen"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-ne-rNP/strings.xml b/WallpaperPicker/res/values-ne-rNP/strings.xml
deleted file mode 100644
index fca4886..0000000
--- a/WallpaperPicker/res/values-ne-rNP/strings.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"वालपेपर मिलाउनुहोस्"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"तस्बिर लोड गर्न सकिएन"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"तस्बिरलाई वालपेपरका रूपमा लोड गर्न सकिएन"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"छविलाई वालपेपरको रूपमा सेट गर्न सकिएन"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d चयन गरियो</item>
-      <item quantity="one">%1$d चयन गरियो</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$d को %1$d वालपेपर"</string>
-    <string name="announce_selection" msgid="123723511662250539">"चयन गरिएको <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"मेट्नुहोस्"</string>
-    <string name="pick_image" msgid="3189640419551368385">"मेरा तस्बिरहरू"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"वालपेपरहरु"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"वालपेपर काँटछाट गर्नुहोस्"</string>
-    <string name="which_wallpaper_option_home_screen" msgid="5259213374485080595">"गृह स्क्रिन"</string>
-    <string name="which_wallpaper_option_lock_screen" msgid="5474588303389139825">"लक स्क्रिन"</string>
-    <string name="which_wallpaper_option_home_screen_and_lock_screen" msgid="7652312651094808607">"गृह स्क्रिन र लक स्क्रिन"</string>
-</resources>
diff --git a/WallpaperPicker/res/values-nl/strings.xml b/WallpaperPicker/res/values-nl/strings.xml
deleted file mode 100644
index 7246b22..0000000
--- a/WallpaperPicker/res/values-nl/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Achtergrond instellen"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Kan afbeelding niet laden"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Kan afbeelding niet laden als achtergrond"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kan afbeelding niet instellen als achtergrond"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d geselecteerd</item>
-      <item quantity="one">%1$d geselecteerd</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Achtergrond %1$d van %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> is geselecteerd"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Verwijderen"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Mijn foto\'s"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Achtergronden"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Achtergrond bijsnijden"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-nodpi/wallpapers.xml b/WallpaperPicker/res/values-nodpi/wallpapers.xml
deleted file mode 100644
index 1e340e4..0000000
--- a/WallpaperPicker/res/values-nodpi/wallpapers.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2009 Google Inc.
- *
- * 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.
- -->
-
-<resources>
-    <string-array name="wallpapers" translatable="false">
-    </string-array>
-</resources>
diff --git a/WallpaperPicker/res/values-pa-rIN/strings.xml b/WallpaperPicker/res/values-pa-rIN/strings.xml
deleted file mode 100644
index 3532fcb..0000000
--- a/WallpaperPicker/res/values-pa-rIN/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"ਵਾਲਪੇਪਰ ਸੈਟ ਕਰੋ"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"ਚਿੱਤਰ ਲੋਡ ਨਹੀਂ ਕਰ ਸਕਿਆ"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"ਵਾਲਪੇਪਰ ਦੇ ਤੌਰ ਤੇ ਚਿੱਤਰ ਲੋਡ ਨਹੀਂ ਕਰ ਸਕਿਆ"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"ਵਾਲਪੇਪਰ ਦੇ ਤੌਰ ਤੇ ਚਿੱਤਰ ਸੈਟ ਨਹੀਂ ਕਰ ਸਕਿਆ"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d ਚੁਣਿਆ ਗਿਆ</item>
-      <item quantity="other">%1$d ਚੁਣੇ ਗਏ</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"ਵਾਲਪੇਪਰ %2$d ਦਾ %1$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> ਚੁਣਿਆ ਗਿਆ"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"ਮਿਟਾਓ"</string>
-    <string name="pick_image" msgid="3189640419551368385">"ਮੇਰੀਆਂ ਫੋਟੋਆਂ"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"ਵਾਲਪੇਪਰ"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"ਵਾਲਪੇਪਰ ਕੱਟੋ"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-pl/strings.xml b/WallpaperPicker/res/values-pl/strings.xml
deleted file mode 100644
index 9815363..0000000
--- a/WallpaperPicker/res/values-pl/strings.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Ustaw tapetę"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Nie udało się załadować obrazu"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Nie udało się załadować obrazu jako tapety"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nie udało się ustawić obrazu jako tapety"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="few">Wybrano %1$d elementy</item>
-      <item quantity="many">Wybrano %1$d elementów</item>
-      <item quantity="other">Wybrano %1$d elementu</item>
-      <item quantity="one">Wybrano %1$d element</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Tapeta %1$d z %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Wybrano <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Usuń"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Moje zdjęcia"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Tapety"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Przytnij tapetę"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-pt-rPT/strings.xml b/WallpaperPicker/res/values-pt-rPT/strings.xml
deleted file mode 100644
index f64815e..0000000
--- a/WallpaperPicker/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Definir imagem fundo"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Não foi possível carregar a imagem"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Não foi possível carregar a imagem como imagem de fundo"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Não foi possível definir a imagem como imagem de fundo"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d selecionadas</item>
-      <item quantity="one">%1$d selecionada</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Imagem de fundo %1$d de %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> selecionada"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Eliminar"</string>
-    <string name="pick_image" msgid="3189640419551368385">"As minhas fotos"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Imagens de fundo"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Recortar imagem de fundo"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-pt/strings.xml b/WallpaperPicker/res/values-pt/strings.xml
deleted file mode 100644
index be573ae..0000000
--- a/WallpaperPicker/res/values-pt/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Definir plano de fundo"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Não foi possível carregar a imagem"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Não foi possível carregar a imagem como plano de fundo"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Não foi possível definir a imagem como plano de fundo"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d selecionados</item>
-      <item quantity="other">%1$d selecionados</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Plano de fundo %1$d de %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> selecionado"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Excluir"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Minhas fotos"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Planos de fundo"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Cortar plano de fundo"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-ro/strings.xml b/WallpaperPicker/res/values-ro/strings.xml
deleted file mode 100644
index 7594bf7..0000000
--- a/WallpaperPicker/res/values-ro/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Setați imaginea de fundal"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Nu s-a putut încărca imaginea"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Nu s-a putut încărca imaginea ca fundal"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nu s-a putut seta ca imagine de fundal"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="few">%1$d selectate</item>
-      <item quantity="other">%1$d selectate</item>
-      <item quantity="one">%1$d selectată</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Imaginea de fundal %1$d din %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"S-a selectat <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Ștergeți"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Fotografiile mele"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Imagini de fundal"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Decupați imaginea de fundal"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-ru/strings.xml b/WallpaperPicker/res/values-ru/strings.xml
deleted file mode 100644
index e8e9972..0000000
--- a/WallpaperPicker/res/values-ru/strings.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Установить как обои"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Не удалось загрузить изображение"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Не удалось загрузить изображение"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Не удалось сменить обои"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">Выбрано: %1$d</item>
-      <item quantity="few">Выбрано: %1$d</item>
-      <item quantity="many">Выбрано: %1$d</item>
-      <item quantity="other">Выбрано: %1$d</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Обои %1$d из %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Выбран элемент \"<xliff:g id="LABEL">%1$s</xliff:g>\""</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Удалить"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Мои фото"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Обои"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Кадрировать обои"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-si-rLK/strings.xml b/WallpaperPicker/res/values-si-rLK/strings.xml
deleted file mode 100644
index ca85089..0000000
--- a/WallpaperPicker/res/values-si-rLK/strings.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"වෝල්පේපරය සකසන්න"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"පින්තූරය පූරණය කිරීමට නොහැකි විය"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"පින්තූරය වෝල්පේපරයක් ලෙස පූරණය කිරීමට නොහැකි විය"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"පින්තූරය බිතුපතක් ලෙස සැකසීමට නොහැකි විය"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$dක් තෝරා ගන්නා ලදී</item>
-      <item quantity="other">%1$dක් තෝරා ගන්නා ලදී</item>
-    </plurals>
-    <!-- String.format failed for translation -->
-    <!-- no translation found for wallpaper_accessibility_name (4093221025304876354) -->
-    <skip />
-    <string name="announce_selection" msgid="123723511662250539">"තෝරාගත්තේ <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"මකන්න"</string>
-    <string name="pick_image" msgid="3189640419551368385">"මගේ ඡායාරූප"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"වෝල්පේපර"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"වෝල්පේපරය කප්පාදු කිරීම"</string>
-    <string name="which_wallpaper_option_home_screen" msgid="5259213374485080595">"මුල් පිටු තිරය"</string>
-    <string name="which_wallpaper_option_lock_screen" msgid="5474588303389139825">"අගුලු තිරය"</string>
-    <string name="which_wallpaper_option_home_screen_and_lock_screen" msgid="7652312651094808607">"මුල් පිටු තිරය සහ අගුලු තිරය"</string>
-</resources>
diff --git a/WallpaperPicker/res/values-sk/strings.xml b/WallpaperPicker/res/values-sk/strings.xml
deleted file mode 100644
index 69aa6f9..0000000
--- a/WallpaperPicker/res/values-sk/strings.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Nastaviť tapetu"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Obrázok nie je možné načítať"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Obrázok nie je možné načítať ako tapetu"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Obrázok nie je možné nastaviť ako tapetu"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="few">Počet vybratých položiek: %1$d</item>
-      <item quantity="many">Počet vybratých položiek: %1$d</item>
-      <item quantity="other">Počet vybratých položiek: %1$d</item>
-      <item quantity="one">Počet vybratých položiek: %1$d</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Tapeta %1$d z %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Vybratá položka <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Odstrániť"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Moje fotky"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Tapety"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Orezanie tapety"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-sl/strings.xml b/WallpaperPicker/res/values-sl/strings.xml
deleted file mode 100644
index 43dea98..0000000
--- a/WallpaperPicker/res/values-sl/strings.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Nastavi ozadje"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Slike ni bilo mogoče naložiti"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Slike ni bilo mogoče naložiti kot ozadje"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Slike ni bilo mogoče nastaviti kot ozadje"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d izbrano</item>
-      <item quantity="two">%1$d izbrani</item>
-      <item quantity="few">%1$d izbrana</item>
-      <item quantity="other">%1$d izbranih</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%1$d. ozadje od %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Izbrano: <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Izbriši"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Moje fotografije"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Ozadja"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Obrezovanje ozadja"</string>
-    <string name="which_wallpaper_option_home_screen" msgid="5259213374485080595">"Začetni zaslon"</string>
-    <string name="which_wallpaper_option_lock_screen" msgid="5474588303389139825">"Zaklenjen zaslon"</string>
-    <string name="which_wallpaper_option_home_screen_and_lock_screen" msgid="7652312651094808607">"Začetni zaslon in zaklenjen zaslon"</string>
-</resources>
diff --git a/WallpaperPicker/res/values-sq-rAL/strings.xml b/WallpaperPicker/res/values-sq-rAL/strings.xml
deleted file mode 100644
index c3cac5b..0000000
--- a/WallpaperPicker/res/values-sq-rAL/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Cakto imazhin e sfondit"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Nuk mund të ngarkonte imazhin"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Nuk mundi të ngarkonte imazhin si imazh sfondi"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nuk mundi të vendoste imazhin si imazh sfondi."</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d të zgjedhur</item>
-      <item quantity="one">%1$d i zgjedhur</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Imazhi i sfondit: %1$d nga gjithsej %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> u përzgjodh"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Fshi"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Fotografitë e mia"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Imazhet e sfondit"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Prit imazhin e sfondit"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-sr/strings.xml b/WallpaperPicker/res/values-sr/strings.xml
deleted file mode 100644
index 28dfd92..0000000
--- a/WallpaperPicker/res/values-sr/strings.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Подеси позадину"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Није могуће учитати слику"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Није могуће учитати слику као позадину"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Учитавање слике као позадине није успело"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">Изабрана је %1$d ставка</item>
-      <item quantity="few">Изабране су %1$d ставке</item>
-      <item quantity="other">Изабрано је %1$d ставки</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Позадина %1$d од %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Изабрана је <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Избриши"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Моје фотографије"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Позадине"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Опсеци позадину"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-sv/strings.xml b/WallpaperPicker/res/values-sv/strings.xml
deleted file mode 100644
index 32c0a5a..0000000
--- a/WallpaperPicker/res/values-sv/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Ange bakgrund"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Det gick inte att läsa in bilden"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Det gick inte att läsa in bilden som bakgrund"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Det gick inte att ange bilden som bakgrund"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d har valts</item>
-      <item quantity="one">%1$d har valts</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Bakgrund %1$d av %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> har valts"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Ta bort"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Mina foton"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Bakgrunder"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Beskär bakgrund"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-sw/strings.xml b/WallpaperPicker/res/values-sw/strings.xml
deleted file mode 100644
index 3294321..0000000
--- a/WallpaperPicker/res/values-sw/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Weka mandhari"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Haikuweza kupakia picha"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Haikuweza kupakia picha iwe mandhari"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Haikuweza kuweka picha kuwa mandhari"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d zimechaguliwa</item>
-      <item quantity="one">%1$d imechaguliwa</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Mandhari %1$d ya %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> iliyochaguliwa"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Futa"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Picha zangu"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Mandhari"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Punguza mandhari"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-sw720dp/styles.xml b/WallpaperPicker/res/values-sw720dp/styles.xml
deleted file mode 100644
index 0058f7e..0000000
--- a/WallpaperPicker/res/values-sw720dp/styles.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources>
-    <style name="BaseWallpaperTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
-        <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:colorBackgroundCacheHint">@null</item>
-        <item name="android:windowShowWallpaper">true</item>
-        <item name="android:windowNoTitle">true</item>
-        <item name="android:windowActionModeOverlay">true</item>
-    </style>
-</resources>
diff --git a/WallpaperPicker/res/values-ta-rIN/strings.xml b/WallpaperPicker/res/values-ta-rIN/strings.xml
deleted file mode 100644
index 5528072..0000000
--- a/WallpaperPicker/res/values-ta-rIN/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"வால்பேப்பரை அமை"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"படத்தை ஏற்ற முடியவில்லை"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"படத்தை வால்பேப்பராக ஏற்ற முடியவில்லை"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"படத்தை வால்பேப்பராக அமைக்க முடியவில்லை"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d தேர்ந்தெடுக்கப்பட்டன</item>
-      <item quantity="one">%1$d தேர்ந்தெடுக்கப்பட்டது</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"வால்பேப்பர் %1$d / %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> தேர்ந்தெடுக்கப்பட்டது"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"நீக்கு"</string>
-    <string name="pick_image" msgid="3189640419551368385">"எனது படங்கள்"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"வால்பேப்பர்கள்"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"வால்பேப்பரைச் செதுக்கு"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-te-rIN/strings.xml b/WallpaperPicker/res/values-te-rIN/strings.xml
deleted file mode 100644
index 0c9e9eb..0000000
--- a/WallpaperPicker/res/values-te-rIN/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"వాల్‌పేపర్‌ను సెట్ చేయి"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"చిత్రాన్ని లోడ్ చేయడం సాధ్యపడలేదు"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"చిత్రాన్ని వాల్‌పేపర్‌గా లోడ్ చేయడం సాధ్యపడలేదు"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"చిత్రాన్ని వాల్‌పేపర్‌గా సెట్ చేయడం సాధ్యపడలేదు"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d ఎంచుకోబడ్డాయి</item>
-      <item quantity="one">%1$d ఎంచుకోబడింది</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$dలో %1$dవ వాల్‌పేపర్"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> ఎంచుకోబడింది"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"తొలగించు"</string>
-    <string name="pick_image" msgid="3189640419551368385">"నా ఫోటోలు"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"వాల్‌పేపర్‌లు"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"వాల్‌పేపర్‌ను కత్తిరించండి"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-th/strings.xml b/WallpaperPicker/res/values-th/strings.xml
deleted file mode 100644
index 48aee2a..0000000
--- a/WallpaperPicker/res/values-th/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"ตั้งวอลเปเปอร์"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"ไม่สามารถโหลดรูปภาพ"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"ไม่สามารถโหลดรูปภาพเป็นวอลเปเปอร์"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"ไม่สามารถตั้งรูปภาพเป็นวอลเปเปอร์"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">เลือกไว้ %1$d รายการ</item>
-      <item quantity="one">เลือกไว้ %1$d รายการ</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"วอลเปเปอร์ %1$d จาก %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"เลือก <xliff:g id="LABEL">%1$s</xliff:g> แล้ว"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"ลบ"</string>
-    <string name="pick_image" msgid="3189640419551368385">"รูปภาพของฉัน"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"วอลเปเปอร์"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"ครอบตัดวอลเปเปอร์"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-tl/strings.xml b/WallpaperPicker/res/values-tl/strings.xml
deleted file mode 100644
index 14bf572..0000000
--- a/WallpaperPicker/res/values-tl/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Itakda ang wallpaper"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Hindi ma-load ang larawan"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Hindi ma-load ang larawan bilang wallpaper"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Hindi maitakda ang larawan bilang wallpaper"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d ang napili</item>
-      <item quantity="other">%1$d ang napili</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Wallpaper %1$d ng %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Napili ang <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Tanggalin"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Aking mga larawan"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Mga Wallpaper"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"I-crop ang wallpaper"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-tr/strings.xml b/WallpaperPicker/res/values-tr/strings.xml
deleted file mode 100644
index 82c6ef8..0000000
--- a/WallpaperPicker/res/values-tr/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Duvar kağıdını ayarla"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Resim yüklenemedi"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Resim duvar kağıdı olarak yüklenemedi"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Resim, duvar kağıdı olarak ayarlanamadı"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d tane seçildi</item>
-      <item quantity="one">%1$d tane seçildi</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"%2$d duvar kağıdı arasından duvar kağıdı %1$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> seçildi"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Sil"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Fotoğraflarım"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Duvar kağıtları"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Duvar kağıdını kırp"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-uk/strings.xml b/WallpaperPicker/res/values-uk/strings.xml
deleted file mode 100644
index b59409a..0000000
--- a/WallpaperPicker/res/values-uk/strings.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Установити фон"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Не вдалося завантажити зображення"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Не вдалося завантажити зображення як фоновий малюнок"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Не вдалося зробити зображення фоновим малюнком"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">Вибрано %1$d</item>
-      <item quantity="few">Вибрано %1$d</item>
-      <item quantity="many">Вибрано %1$d</item>
-      <item quantity="other">Вибрано %1$d</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Фоновий малюнок %1$d з %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"Вибрано <xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Видалити"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Мої фото"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Фонові малюнки"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Обрізати фоновий малюнок"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-ur-rPK/strings.xml b/WallpaperPicker/res/values-ur-rPK/strings.xml
deleted file mode 100644
index 616b36f..0000000
--- a/WallpaperPicker/res/values-ur-rPK/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"وال پیپر سیٹ کریں"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"تصویر کو لوڈ نہیں کیا جا سکا"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"تصویر کو وال پیپر کے بطور لوڈ نہیں کیا جا سکا"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"تصویر کو بطور وال پیپر سیٹ نہیں کیا جا سکا"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">‏%1$d کا انتخاب ہو گیا</item>
-      <item quantity="one">‏%1$d کا انتخاب ہو گیا</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"‏وال پیپر ‎%1$d از ‎%2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> کو منتخب کیا گیا"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"حذف کریں"</string>
-    <string name="pick_image" msgid="3189640419551368385">"میری تصاویر"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"وال پیپرز"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"وال پیپر کو تراشیں"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-uz-rUZ/strings.xml b/WallpaperPicker/res/values-uz-rUZ/strings.xml
deleted file mode 100644
index 45c9e98..0000000
--- a/WallpaperPicker/res/values-uz-rUZ/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Fonga rasm o‘rnatish"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Rasm yuklanmadi"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Fon rasmi sifatida rasm yuklanmadi"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Rasmni fon rasmi sifatida o‘rnatib bo‘lmadi"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">%1$d ta tanlandi</item>
-      <item quantity="one">%1$d ta tanlandi</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Fon rasmi %2$ddan %1$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> tanlandi"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"O‘chirish"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Mening rasmlarim"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Fon rasmlari"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Fon rasmini kesish"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-v19/styles.xml b/WallpaperPicker/res/values-v19/styles.xml
deleted file mode 100644
index 15fb0ea..0000000
--- a/WallpaperPicker/res/values-v19/styles.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources>
-    <style name="Theme.WallpaperCropper" parent="@android:style/Theme.DeviceDefault">
-        <item name="android:actionBarStyle">@style/WallpaperCropperActionBar</item>
-        <item name="android:windowFullscreen">true</item>
-        <item name="android:windowActionBarOverlay">true</item>
-        <item name="android:windowTranslucentNavigation">true</item>
-    </style>
-
-    <style name="Theme" parent="@style/BaseWallpaperTheme">
-        <item name="android:windowTranslucentStatus">true</item>
-        <item name="android:windowTranslucentNavigation">true</item>
-    </style>
-</resources>
diff --git a/WallpaperPicker/res/values-v21/styles.xml b/WallpaperPicker/res/values-v21/styles.xml
deleted file mode 100644
index d2f7dd4..0000000
--- a/WallpaperPicker/res/values-v21/styles.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2015 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.
-*/
--->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <style name="WallpaperCropperActionBar" parent="@android:style/Widget.DeviceDefault.ActionBar">
-        <item name="android:displayOptions">showCustom</item>
-        <item name="android:background">#88000000</item>
-        <item name="android:contentInsetEnd">0dp</item>
-        <item name="android:contentInsetLeft">0dp</item>
-        <item name="android:contentInsetRight">0dp</item>
-        <item name="android:contentInsetStart">0dp</item>
-    </style>
-
-    <style name="ActionBarSetWallpaperStyle" parent="@android:style/Widget.DeviceDefault.ActionButton">
-        <item name="android:textColor">#ffffffff</item>
-        <item name="android:background">?android:attr/selectableItemBackgroundBorderless</item>
-    </style>
-
-    <style name="Theme" parent="@style/BaseWallpaperTheme">
-        <item name="android:windowTranslucentStatus">false</item>
-        <item name="android:windowTranslucentNavigation">false</item>
-        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
-        <item name="android:statusBarColor">#00000000</item>
-        <item name="android:navigationBarColor">#00000000</item>
-        <item name="android:colorControlActivated">@color/launcher_accent_color</item>
-        <item name="android:colorAccent">@color/launcher_accent_color</item>
-        <item name="android:colorPrimary">@color/launcher_accent_color</item>
-    </style>
-</resources>
\ No newline at end of file
diff --git a/WallpaperPicker/res/values-vi/strings.xml b/WallpaperPicker/res/values-vi/strings.xml
deleted file mode 100644
index 5aafc06..0000000
--- a/WallpaperPicker/res/values-vi/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Đặt hình nền"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Không thể tải hình ảnh"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Không thể tải hình ảnh làm hình nền"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Không thể đặt hình ảnh làm hình nền"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">Đã chọn %1$d mục</item>
-      <item quantity="one">Đã chọn %1$d mục</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Hình nền %1$d / %2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> được chọn"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Xóa"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Ảnh của tôi"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Hình nền"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Cắt hình nền"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-zh-rCN/strings.xml b/WallpaperPicker/res/values-zh-rCN/strings.xml
deleted file mode 100644
index 9b9b509..0000000
--- a/WallpaperPicker/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"设置壁纸"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"无法加载图片"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"无法加载要设为壁纸的图片"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"无法将图片设为壁纸"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">已选择 %1$d 项</item>
-      <item quantity="one">已选择 %1$d 项</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"第%1$d张壁纸,共%2$d张"</string>
-    <string name="announce_selection" msgid="123723511662250539">"已选择<xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"删除"</string>
-    <string name="pick_image" msgid="3189640419551368385">"我的照片"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"壁纸"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"剪裁壁纸"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-zh-rHK/strings.xml b/WallpaperPicker/res/values-zh-rHK/strings.xml
deleted file mode 100644
index c3c2271..0000000
--- a/WallpaperPicker/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"設定桌布"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"無法載入圖片"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"無法載入圖片設為桌布"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"無法將圖片設為桌布"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">已選取 %1$d 張</item>
-      <item quantity="one">已選取 %1$d 張</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"第 %1$d 張桌布,共 %2$d 張"</string>
-    <string name="announce_selection" msgid="123723511662250539">"已選取<xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"刪除"</string>
-    <string name="pick_image" msgid="3189640419551368385">"我的相片"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"桌布"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"裁剪桌布"</string>
-    <string name="which_wallpaper_option_home_screen" msgid="5259213374485080595">"主畫面"</string>
-    <string name="which_wallpaper_option_lock_screen" msgid="5474588303389139825">"上鎖畫面"</string>
-    <string name="which_wallpaper_option_home_screen_and_lock_screen" msgid="7652312651094808607">"主畫面和上鎖畫面"</string>
-</resources>
diff --git a/WallpaperPicker/res/values-zh-rTW/strings.xml b/WallpaperPicker/res/values-zh-rTW/strings.xml
deleted file mode 100644
index b22c691..0000000
--- a/WallpaperPicker/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"設定桌布"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"無法載入圖片"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"無法載入您要設為桌布的圖片"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"無法將圖片設為桌布"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="other">已選取 %1$d 個</item>
-      <item quantity="one">已選取 %1$d 個</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"第 %1$d 張桌布,共 %2$d 張"</string>
-    <string name="announce_selection" msgid="123723511662250539">"已選取<xliff:g id="LABEL">%1$s</xliff:g>"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"刪除"</string>
-    <string name="pick_image" msgid="3189640419551368385">"我的相片"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"桌布"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"裁剪桌布"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values-zu/strings.xml b/WallpaperPicker/res/values-zu/strings.xml
deleted file mode 100644
index 0069293..0000000
--- a/WallpaperPicker/res/values-zu/strings.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="wallpaper_instructions" msgid="3524143401182707094">"Setha isithombe sangemuva"</string>
-    <string name="image_load_fail" msgid="7538534580694411837">"Ayikwazanga ukulayisha isithombe"</string>
-    <string name="wallpaper_load_fail" msgid="4800700444605404650">"Ayikwazanga ukulayisha isithombe njengesithombe sangemuva"</string>
-    <string name="wallpaper_set_fail" msgid="7023180794008631780">"Ayikwazanga ukusetha isithombe njengesithombe sangemuva"</string>
-    <plurals name="number_of_items_selected" formatted="false" msgid="4578652015328149361">
-      <item quantity="one">%1$d okukhethiwe</item>
-      <item quantity="other">%1$d okukhethiwe</item>
-    </plurals>
-    <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"Isithombe sangemuva esingu-%1$d kwezingu-%2$d"</string>
-    <string name="announce_selection" msgid="123723511662250539">"I-<xliff:g id="LABEL">%1$s</xliff:g> ekhethiwe"</string>
-    <string name="wallpaper_delete" msgid="1459353972739215344">"Susa"</string>
-    <string name="pick_image" msgid="3189640419551368385">"Izithombe zami"</string>
-    <string name="pick_wallpaper" msgid="4628969645948454559">"Izithombe zangemuva"</string>
-    <string name="crop_wallpaper" msgid="4882870800623585836">"Nqampuna isithombe sangemuva"</string>
-    <!-- no translation found for which_wallpaper_option_home_screen (5259213374485080595) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_lock_screen (5474588303389139825) -->
-    <skip />
-    <!-- no translation found for which_wallpaper_option_home_screen_and_lock_screen (7652312651094808607) -->
-    <skip />
-</resources>
diff --git a/WallpaperPicker/res/values/arrays.xml b/WallpaperPicker/res/values/arrays.xml
deleted file mode 100644
index 5c10b98..0000000
--- a/WallpaperPicker/res/values/arrays.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
--->
-<resources>
-    <string-array name="which_wallpaper_options">
-        <item>@string/which_wallpaper_option_home_screen</item>
-        <item>@string/which_wallpaper_option_lock_screen</item>
-        <item>@string/which_wallpaper_option_home_screen_and_lock_screen</item>
-    </string-array>
-</resources>
\ No newline at end of file
diff --git a/WallpaperPicker/res/values/colors.xml b/WallpaperPicker/res/values/colors.xml
deleted file mode 100644
index 6ba32f0..0000000
--- a/WallpaperPicker/res/values/colors.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/* //device/apps/common/assets/res/any/colors.xml
-**
-** Copyright 2013, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
-**
-**     http://www.apache.org/licenses/LICENSE-2.0 
-**
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
-** limitations under the License.
-*/
--->
-<resources>
-    <color name="wallpaper_picker_translucent_gray">#66000000</color>
-
-    <color name="launcher_accent_color">#ff009688</color>
-</resources>
diff --git a/WallpaperPicker/res/values/config.xml b/WallpaperPicker/res/values/config.xml
deleted file mode 100644
index 2f5174c..0000000
--- a/WallpaperPicker/res/values/config.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <!-- Specifies whether to expand the cropped area on both sides (rather
-         than just to one side) -->
-    <bool name="center_crop">false</bool>
-</resources>
diff --git a/WallpaperPicker/res/values/dimens.xml b/WallpaperPicker/res/values/dimens.xml
deleted file mode 100644
index 0447c6d..0000000
--- a/WallpaperPicker/res/values/dimens.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-  
-          http://www.apache.org/licenses/LICENSE-2.0
-  
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-<!-- Wallpaper picker -->
-    <dimen name="wallpaperThumbnailWidth">106.5dp</dimen>
-    <dimen name="wallpaperThumbnailHeight">94.5dp</dimen>
-    <dimen name="wallpaperItemIconSize">32dp</dimen>
-</resources>
diff --git a/WallpaperPicker/res/values/strings.xml b/WallpaperPicker/res/values/strings.xml
deleted file mode 100644
index aef6a1a..0000000
--- a/WallpaperPicker/res/values/strings.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Button label on Wallpaper picker screen; user selects this button to set a specific wallpaper -->
-    <string name="wallpaper_instructions">Set wallpaper</string>
-    <!-- Error message when an image is selected as a wallpaper,
-         but the wallpaper picker cannot load it -->
-    <string name="image_load_fail">Coudn\'t load image</string>
-    <!-- Error message when an image is selected as a wallpaper,
-         but the wallpaper cropper cannot load it. The user will
-         usually see this when using another app and trying to set
-         an image as the wallpaper -->
-    <string name="wallpaper_load_fail">Couldn\'t load image as wallpaper</string>
-    <!-- Error message when an image is selected as a wallpaper,
-         but something goes wrong when the user clicks "Set wallpaper" -->
-    <string name="wallpaper_set_fail">Couldn\'t set image as wallpaper</string>
-    <!-- Shown when wallpapers are selected in Wallpaper picker -->
-    <!-- String indicating how many media item(s) is(are) selected
-            eg. 1 selected [CHAR LIMIT=30] -->
-    <plurals name="number_of_items_selected">
-        <item quantity="zero">%1$d selected</item>
-        <item quantity="one">%1$d selected</item>
-        <item quantity="other">%1$d selected</item>
-    </plurals>
-    <!-- Accessibility string used as a label for a particular wallpaper in the Wallpaper Picker list.
-         e.g. "Wallpaper 3 of 10" -->
-    <string name="wallpaper_accessibility_name">Wallpaper %1$d of %2$d</string>
-    <!-- Accessibility string used to announce that a wallpaper has been selected. -->
-    <string name="announce_selection">Selected <xliff:g id="label" example="Wallpaper 3 of 10">%1$s</xliff:g></string>
-
-    <!-- Label on button to delete wallpaper(s) -->
-    <string name="wallpaper_delete">Delete</string>
-    <!-- Label on button in Wallpaper Picker to pick an image -->
-    <string name="pick_image">My photos</string>
-    <!-- Option in "Select wallpaper from" dialog box -->
-    <string name="pick_wallpaper">Wallpapers</string>
-    <!-- Title of activity for cropping wallpapers -->
-    <string name="crop_wallpaper">Crop wallpaper</string>
-
-    <!-- Option for setting the wallpaper only on the home screen. -->
-    <string name="which_wallpaper_option_home_screen">Home screen</string>
-    <!-- Option for setting the wallpaper only on the lock screen. -->
-    <string name="which_wallpaper_option_lock_screen">Lock screen</string>
-    <!-- Option for setting the wallpaper on both the home screen and lock screen. -->
-    <string name="which_wallpaper_option_home_screen_and_lock_screen">Home screen and lock screen</string>
-</resources>
diff --git a/WallpaperPicker/res/values/styles.xml b/WallpaperPicker/res/values/styles.xml
deleted file mode 100644
index d1c945a..0000000
--- a/WallpaperPicker/res/values/styles.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <style name="Theme.WallpaperCropper" parent="@android:style/Theme.DeviceDefault">
-        <item name="android:actionBarStyle">@style/WallpaperCropperActionBar</item>
-        <item name="android:windowFullscreen">true</item>
-        <item name="android:windowActionBarOverlay">true</item>
-    </style>
-
-    <style name="Theme.WallpaperPicker" parent="Theme.WallpaperCropper">
-        <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:colorBackgroundCacheHint">@null</item>
-        <item name="android:windowShowWallpaper">true</item>
-    </style>
-
-    <style name="WallpaperCropperActionBar" parent="@android:style/Widget.DeviceDefault.ActionBar">
-        <item name="android:displayOptions">showCustom</item>
-        <item name="android:background">#88000000</item>
-    </style>
-
-    <style name="BaseWallpaperTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
-        <item name="android:windowBackground">@android:color/transparent</item>
-        <item name="android:colorBackgroundCacheHint">@null</item>
-        <item name="android:windowShowWallpaper">true</item>
-        <item name="android:windowNoTitle">true</item>
-    </style>
-
-    <style name="Theme" parent="@style/BaseWallpaperTheme"></style>
-
-    <style name="ActionBarSetWallpaperStyle" parent="@android:style/Widget.DeviceDefault.ActionButton">
-        <item name="android:textColor">#ffffffff</item>
-        <item name="android:background">?android:attr/selectableItemBackground</item>
-    </style>
-</resources>
diff --git a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java b/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
deleted file mode 100644
index 153fa90..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
+++ /dev/null
@@ -1,427 +0,0 @@
-/**
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.gallery3d.common;
-
-import android.app.WallpaperManager;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.CompressFormat;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapRegionDecoder;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.util.Log;
-import android.widget.Toast;
-
-import com.android.launcher3.NycWallpaperUtils;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-
-public class BitmapCropTask extends AsyncTask<Integer, Void, Boolean> {
-
-    public interface OnBitmapCroppedHandler {
-        public void onBitmapCropped(byte[] imageBytes, Rect cropHint);
-    }
-
-    public interface OnEndCropHandler {
-        public void run(boolean cropSucceeded);
-    }
-
-    private static final int DEFAULT_COMPRESS_QUALITY = 90;
-    private static final String LOGTAG = "BitmapCropTask";
-
-    Uri mInUri = null;
-    Context mContext;
-    String mInFilePath;
-    byte[] mInImageBytes;
-    int mInResId = 0;
-    RectF mCropBounds = null;
-    int mOutWidth, mOutHeight;
-    int mRotation;
-    boolean mSetWallpaper;
-    boolean mSaveCroppedBitmap;
-    Bitmap mCroppedBitmap;
-    BitmapCropTask.OnEndCropHandler mOnEndCropHandler;
-    Resources mResources;
-    BitmapCropTask.OnBitmapCroppedHandler mOnBitmapCroppedHandler;
-    boolean mNoCrop;
-
-    public BitmapCropTask(byte[] imageBytes,
-            RectF cropBounds, int rotation, int outWidth, int outHeight,
-            boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) {
-        mInImageBytes = imageBytes;
-        init(cropBounds, rotation,
-                outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndCropHandler);
-    }
-
-    public BitmapCropTask(Context c, Uri inUri,
-            RectF cropBounds, int rotation, int outWidth, int outHeight,
-            boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) {
-        mContext = c;
-        mInUri = inUri;
-        init(cropBounds, rotation,
-                outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndCropHandler);
-    }
-
-    public BitmapCropTask(Context c, Resources res, int inResId,
-            RectF cropBounds, int rotation, int outWidth, int outHeight,
-            boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) {
-        mContext = c;
-        mInResId = inResId;
-        mResources = res;
-        init(cropBounds, rotation,
-                outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndCropHandler);
-    }
-
-    private void init(RectF cropBounds, int rotation, int outWidth, int outHeight,
-            boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) {
-        mCropBounds = cropBounds;
-        mRotation = rotation;
-        mOutWidth = outWidth;
-        mOutHeight = outHeight;
-        mSetWallpaper = setWallpaper;
-        mSaveCroppedBitmap = saveCroppedBitmap;
-        mOnEndCropHandler = onEndCropHandler;
-    }
-
-    public void setOnBitmapCropped(BitmapCropTask.OnBitmapCroppedHandler handler) {
-        mOnBitmapCroppedHandler = handler;
-    }
-
-    public void setNoCrop(boolean value) {
-        mNoCrop = value;
-    }
-
-    public void setOnEndRunnable(OnEndCropHandler onEndCropHandler) {
-        mOnEndCropHandler = onEndCropHandler;
-    }
-
-    // Helper to setup input stream
-    private InputStream regenerateInputStream() {
-        if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) {
-            Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " +
-                    "image byte array given");
-        } else {
-            try {
-                if (mInUri != null) {
-                    return new BufferedInputStream(
-                            mContext.getContentResolver().openInputStream(mInUri));
-                } else if (mInFilePath != null) {
-                    return mContext.openFileInput(mInFilePath);
-                } else if (mInImageBytes != null) {
-                    return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes));
-                } else {
-                    return new BufferedInputStream(mResources.openRawResource(mInResId));
-                }
-            } catch (FileNotFoundException e) {
-                Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e);
-            }
-        }
-        return null;
-    }
-
-    public Point getImageBounds() {
-        InputStream is = regenerateInputStream();
-        if (is != null) {
-            BitmapFactory.Options options = new BitmapFactory.Options();
-            options.inJustDecodeBounds = true;
-            BitmapFactory.decodeStream(is, null, options);
-            Utils.closeSilently(is);
-            if (options.outWidth != 0 && options.outHeight != 0) {
-                return new Point(options.outWidth, options.outHeight);
-            }
-        }
-        return null;
-    }
-
-    public void setCropBounds(RectF cropBounds) {
-        mCropBounds = cropBounds;
-    }
-
-    public Bitmap getCroppedBitmap() {
-        return mCroppedBitmap;
-    }
-    public boolean cropBitmap(int whichWallpaper) {
-        boolean failure = false;
-
-        if (mSetWallpaper && mNoCrop) {
-            try {
-                InputStream is = regenerateInputStream();
-                setWallpaper(is, null, whichWallpaper);
-                Utils.closeSilently(is);
-            } catch (IOException e) {
-                Log.w(LOGTAG, "cannot write stream to wallpaper", e);
-                failure = true;
-            }
-            return !failure;
-        } else if (mSetWallpaper && Utilities.isNycOrAbove()
-                && mRotation == 0 && mOutWidth > 0 && mOutHeight > 0) {
-            Rect hint = new Rect();
-            mCropBounds.roundOut(hint);
-
-            InputStream is = null;
-            try {
-                is = regenerateInputStream();
-                if (is == null) {
-                    Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
-                    failure = true;
-                    return false;
-                }
-                WallpaperManager.getInstance(mContext).suggestDesiredDimensions(mOutWidth, mOutHeight);
-                setWallpaper(is, hint, whichWallpaper);
-
-                if (mOnBitmapCroppedHandler != null) {
-                    mOnBitmapCroppedHandler.onBitmapCropped(null, hint);
-                }
-
-                failure = false;
-            } catch (IOException e) {
-                Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
-            } finally {
-                Utils.closeSilently(is);
-            }
-        } else {
-            // Find crop bounds (scaled to original image size)
-            Rect roundedTrueCrop = new Rect();
-            Matrix rotateMatrix = new Matrix();
-            Matrix inverseRotateMatrix = new Matrix();
-
-            Point bounds = getImageBounds();
-            if (mRotation > 0) {
-                rotateMatrix.setRotate(mRotation);
-                inverseRotateMatrix.setRotate(-mRotation);
-
-                mCropBounds.roundOut(roundedTrueCrop);
-                mCropBounds = new RectF(roundedTrueCrop);
-
-                if (bounds == null) {
-                    Log.w(LOGTAG, "cannot get bounds for image");
-                    failure = true;
-                    return false;
-                }
-
-                float[] rotatedBounds = new float[] { bounds.x, bounds.y };
-                rotateMatrix.mapPoints(rotatedBounds);
-                rotatedBounds[0] = Math.abs(rotatedBounds[0]);
-                rotatedBounds[1] = Math.abs(rotatedBounds[1]);
-
-                mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2);
-                inverseRotateMatrix.mapRect(mCropBounds);
-                mCropBounds.offset(bounds.x/2, bounds.y/2);
-            }
-
-            mCropBounds.roundOut(roundedTrueCrop);
-
-            if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
-                Log.w(LOGTAG, "crop has bad values for full size image");
-                failure = true;
-                return false;
-            }
-
-            // See how much we're reducing the size of the image
-            int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth,
-                    roundedTrueCrop.height() / mOutHeight));
-            // Attempt to open a region decoder
-            BitmapRegionDecoder decoder = null;
-            InputStream is = null;
-            try {
-                is = regenerateInputStream();
-                if (is == null) {
-                    Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
-                    failure = true;
-                    return false;
-                }
-                decoder = BitmapRegionDecoder.newInstance(is, false);
-                Utils.closeSilently(is);
-            } catch (IOException e) {
-                Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
-            } finally {
-               Utils.closeSilently(is);
-               is = null;
-            }
-
-            Bitmap crop = null;
-            if (decoder != null) {
-                // Do region decoding to get crop bitmap
-                BitmapFactory.Options options = new BitmapFactory.Options();
-                if (scaleDownSampleSize > 1) {
-                    options.inSampleSize = scaleDownSampleSize;
-                }
-                crop = decoder.decodeRegion(roundedTrueCrop, options);
-                decoder.recycle();
-            }
-
-            if (crop == null) {
-                // BitmapRegionDecoder has failed, try to crop in-memory
-                is = regenerateInputStream();
-                Bitmap fullSize = null;
-                if (is != null) {
-                    BitmapFactory.Options options = new BitmapFactory.Options();
-                    if (scaleDownSampleSize > 1) {
-                        options.inSampleSize = scaleDownSampleSize;
-                    }
-                    fullSize = BitmapFactory.decodeStream(is, null, options);
-                    Utils.closeSilently(is);
-                }
-                if (fullSize != null) {
-                    // Find out the true sample size that was used by the decoder
-                    scaleDownSampleSize = bounds.x / fullSize.getWidth();
-                    mCropBounds.left /= scaleDownSampleSize;
-                    mCropBounds.top /= scaleDownSampleSize;
-                    mCropBounds.bottom /= scaleDownSampleSize;
-                    mCropBounds.right /= scaleDownSampleSize;
-                    mCropBounds.roundOut(roundedTrueCrop);
-
-                    // Adjust values to account for issues related to rounding
-                    if (roundedTrueCrop.width() > fullSize.getWidth()) {
-                        // Adjust the width
-                        roundedTrueCrop.right = roundedTrueCrop.left + fullSize.getWidth();
-                    }
-                    if (roundedTrueCrop.right > fullSize.getWidth()) {
-                        // Adjust the left and right values.
-                        roundedTrueCrop.offset(-(roundedTrueCrop.right - fullSize.getWidth()), 0);
-                    }
-                    if (roundedTrueCrop.height() > fullSize.getHeight()) {
-                        // Adjust the height
-                        roundedTrueCrop.bottom = roundedTrueCrop.top + fullSize.getHeight();
-                    }
-                    if (roundedTrueCrop.bottom > fullSize.getHeight()) {
-                        // Adjust the top and bottom values.
-                        roundedTrueCrop.offset(0, -(roundedTrueCrop.bottom - fullSize.getHeight()));
-                    }
-
-                    crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
-                            roundedTrueCrop.top, roundedTrueCrop.width(),
-                            roundedTrueCrop.height());
-                }
-            }
-
-            if (crop == null) {
-                Log.w(LOGTAG, "cannot decode file: " + mInUri.toString());
-                failure = true;
-                return false;
-            }
-            if (mOutWidth > 0 && mOutHeight > 0 || mRotation > 0) {
-                float[] dimsAfter = new float[] { crop.getWidth(), crop.getHeight() };
-                rotateMatrix.mapPoints(dimsAfter);
-                dimsAfter[0] = Math.abs(dimsAfter[0]);
-                dimsAfter[1] = Math.abs(dimsAfter[1]);
-
-                if (!(mOutWidth > 0 && mOutHeight > 0)) {
-                    mOutWidth = Math.round(dimsAfter[0]);
-                    mOutHeight = Math.round(dimsAfter[1]);
-                }
-
-                RectF cropRect = new RectF(0, 0, dimsAfter[0], dimsAfter[1]);
-                RectF returnRect = new RectF(0, 0, mOutWidth, mOutHeight);
-
-                Matrix m = new Matrix();
-                if (mRotation == 0) {
-                    m.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
-                } else {
-                    Matrix m1 = new Matrix();
-                    m1.setTranslate(-crop.getWidth() / 2f, -crop.getHeight() / 2f);
-                    Matrix m2 = new Matrix();
-                    m2.setRotate(mRotation);
-                    Matrix m3 = new Matrix();
-                    m3.setTranslate(dimsAfter[0] / 2f, dimsAfter[1] / 2f);
-                    Matrix m4 = new Matrix();
-                    m4.setRectToRect(cropRect, returnRect, Matrix.ScaleToFit.FILL);
-
-                    Matrix c1 = new Matrix();
-                    c1.setConcat(m2, m1);
-                    Matrix c2 = new Matrix();
-                    c2.setConcat(m4, m3);
-                    m.setConcat(c2, c1);
-                }
-
-                Bitmap tmp = Bitmap.createBitmap((int) returnRect.width(),
-                        (int) returnRect.height(), Bitmap.Config.ARGB_8888);
-                if (tmp != null) {
-                    Canvas c = new Canvas(tmp);
-                    Paint p = new Paint();
-                    p.setFilterBitmap(true);
-                    c.drawBitmap(crop, m, p);
-                    crop = tmp;
-                }
-            }
-
-            if (mSaveCroppedBitmap) {
-                mCroppedBitmap = crop;
-            }
-
-            // Compress to byte array
-            ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
-            if (crop.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
-                // If we need to set to the wallpaper, set it
-                if (mSetWallpaper) {
-                    try {
-                        byte[] outByteArray = tmpOut.toByteArray();
-                        setWallpaper(new ByteArrayInputStream(outByteArray), null, whichWallpaper);
-                        if (mOnBitmapCroppedHandler != null) {
-                            mOnBitmapCroppedHandler.onBitmapCropped(outByteArray,
-                                    new Rect(0, 0, crop.getWidth(), crop.getHeight()));
-                        }
-                    } catch (IOException e) {
-                        Log.w(LOGTAG, "cannot write stream to wallpaper", e);
-                        failure = true;
-                    }
-                }
-            } else {
-                Log.w(LOGTAG, "cannot compress bitmap");
-                failure = true;
-            }
-        }
-        return !failure; // True if any of the operations failed
-    }
-
-    @Override
-    protected Boolean doInBackground(Integer... params) {
-        return cropBitmap(params.length == 0 ? NycWallpaperUtils.FLAG_SET_SYSTEM : params[0]);
-    }
-
-    @Override
-    protected void onPostExecute(Boolean cropSucceeded) {
-        if (!cropSucceeded) {
-            Toast.makeText(mContext, R.string.wallpaper_set_fail, Toast.LENGTH_SHORT).show();
-        }
-        if (mOnEndCropHandler != null) {
-            mOnEndCropHandler.run(cropSucceeded);
-        }
-    }
-
-    private void setWallpaper(InputStream in, Rect crop, int whichWallpaper) throws IOException {
-        if (!Utilities.isNycOrAbove()) {
-            WallpaperManager.getInstance(mContext.getApplicationContext()).setStream(in);
-        } else {
-            NycWallpaperUtils.setStream(mContext, in, crop, true, whichWallpaper);
-        }
-    }
-}
\ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java b/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java
deleted file mode 100644
index 9ac5c1b..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.common;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.util.Log;
-
-import com.android.gallery3d.exif.ExifInterface;
-
-import java.io.BufferedInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-public class BitmapUtils {
-
-    private static final String TAG = "BitmapUtils";
-
-    // Find the min x that 1 / x >= scale
-    public static int computeSampleSizeLarger(float scale) {
-        int initialSize = (int) Math.floor(1f / scale);
-        if (initialSize <= 1) return 1;
-
-        return initialSize <= 8
-                ? Utils.prevPowerOf2(initialSize)
-                : initialSize / 8 * 8;
-    }
-
-    public static int getRotationFromExif(Context context, Uri uri) {
-        return BitmapUtils.getRotationFromExifHelper(null, 0, context, uri);
-    }
-
-    public static int getRotationFromExif(Resources res, int resId) {
-        return BitmapUtils.getRotationFromExifHelper(res, resId, null, null);
-    }
-
-    private static int getRotationFromExifHelper(Resources res, int resId, Context context, Uri uri) {
-        ExifInterface ei = new ExifInterface();
-        InputStream is = null;
-        BufferedInputStream bis = null;
-        try {
-            if (uri != null) {
-                is = context.getContentResolver().openInputStream(uri);
-                bis = new BufferedInputStream(is);
-                ei.readExif(bis);
-            } else {
-                is = res.openRawResource(resId);
-                bis = new BufferedInputStream(is);
-                ei.readExif(bis);
-            }
-            Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
-            if (ori != null) {
-                return ExifInterface.getRotationForOrientationValue(ori.shortValue());
-            }
-        } catch (IOException e) {
-            Log.w(TAG, "Getting exif data failed", e);
-        } catch (NullPointerException e) {
-            // Sometimes the ExifInterface has an internal NPE if Exif data isn't valid
-            Log.w(TAG, "Getting exif data failed", e);
-        } finally {
-            Utils.closeSilently(bis);
-            Utils.closeSilently(is);
-        }
-        return 0;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/common/Utils.java b/WallpaperPicker/src/com/android/gallery3d/common/Utils.java
deleted file mode 100644
index 8466c22..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/common/Utils.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.common;
-
-import android.database.Cursor;
-import android.graphics.RectF;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import java.io.Closeable;
-import java.io.IOException;
-
-public class Utils {
-    private static final String TAG = "Utils";
-
-    // Throws AssertionError if the input is false.
-    public static void assertTrue(boolean cond) {
-        if (!cond) {
-            throw new AssertionError();
-        }
-    }
-
-    // Returns the next power of two.
-    // Returns the input if it is already power of 2.
-    // Throws IllegalArgumentException if the input is <= 0 or
-    // the answer overflows.
-    public static int nextPowerOf2(int n) {
-        if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException("n is invalid: " + n);
-        n -= 1;
-        n |= n >> 16;
-        n |= n >> 8;
-        n |= n >> 4;
-        n |= n >> 2;
-        n |= n >> 1;
-        return n + 1;
-    }
-
-    // Returns the previous power of two.
-    // Returns the input if it is already power of 2.
-    // Throws IllegalArgumentException if the input is <= 0
-    public static int prevPowerOf2(int n) {
-        if (n <= 0) throw new IllegalArgumentException();
-        return Integer.highestOneBit(n);
-    }
-
-    // Returns the input value x clamped to the range [min, max].
-    public static int clamp(int x, int min, int max) {
-        if (x > max) return max;
-        if (x < min) return min;
-        return x;
-    }
-
-    public static int ceilLog2(float value) {
-        int i;
-        for (i = 0; i < 31; i++) {
-            if ((1 << i) >= value) break;
-        }
-        return i;
-    }
-
-    public static int floorLog2(float value) {
-        int i;
-        for (i = 0; i < 31; i++) {
-            if ((1 << i) > value) break;
-        }
-        return i - 1;
-    }
-
-    public static void closeSilently(Closeable c) {
-        if (c == null) return;
-        try {
-            c.close();
-        } catch (IOException t) {
-            Log.w(TAG, "close fail ", t);
-        }
-    }
-
-    public static void closeSilently(ParcelFileDescriptor fd) {
-        try {
-            if (fd != null) fd.close();
-        } catch (Throwable t) {
-            Log.w(TAG, "fail to close", t);
-        }
-    }
-
-    public static void closeSilently(Cursor cursor) {
-        try {
-            if (cursor != null) cursor.close();
-        } catch (Throwable t) {
-            Log.w(TAG, "fail to close", t);
-        }
-    }
-
-    public static RectF getMaxCropRect(
-            int inWidth, int inHeight, int outWidth, int outHeight, boolean leftAligned) {
-        RectF cropRect = new RectF();
-        // Get a crop rect that will fit this
-        if (inWidth / (float) inHeight > outWidth / (float) outHeight) {
-             cropRect.top = 0;
-             cropRect.bottom = inHeight;
-             cropRect.left = (inWidth - (outWidth / (float) outHeight) * inHeight) / 2;
-             cropRect.right = inWidth - cropRect.left;
-             if (leftAligned) {
-                 cropRect.right -= cropRect.left;
-                 cropRect.left = 0;
-             }
-        } else {
-            cropRect.left = 0;
-            cropRect.right = inWidth;
-            cropRect.top = (inHeight - (outHeight / (float) outWidth) * inWidth) / 2;
-            cropRect.bottom = inHeight - cropRect.top;
-        }
-        return cropRect;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ByteBufferInputStream.java b/WallpaperPicker/src/com/android/gallery3d/exif/ByteBufferInputStream.java
deleted file mode 100644
index 7fb9f22..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ByteBufferInputStream.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-
-class ByteBufferInputStream extends InputStream {
-
-    private ByteBuffer mBuf;
-
-    public ByteBufferInputStream(ByteBuffer buf) {
-        mBuf = buf;
-    }
-
-    @Override
-    public int read() {
-        if (!mBuf.hasRemaining()) {
-            return -1;
-        }
-        return mBuf.get() & 0xFF;
-    }
-
-    @Override
-    public int read(byte[] bytes, int off, int len) {
-        if (!mBuf.hasRemaining()) {
-            return -1;
-        }
-
-        len = Math.min(len, mBuf.remaining());
-        mBuf.get(bytes, off, len);
-        return len;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/CountedDataInputStream.java b/WallpaperPicker/src/com/android/gallery3d/exif/CountedDataInputStream.java
deleted file mode 100644
index dfd4a1a..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/CountedDataInputStream.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-import java.io.EOFException;
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.Charset;
-
-class CountedDataInputStream extends FilterInputStream {
-
-    private int mCount = 0;
-
-    // allocate a byte buffer for a long value;
-    private final byte mByteArray[] = new byte[8];
-    private final ByteBuffer mByteBuffer = ByteBuffer.wrap(mByteArray);
-
-    protected CountedDataInputStream(InputStream in) {
-        super(in);
-    }
-
-    public int getReadByteCount() {
-        return mCount;
-    }
-
-    @Override
-    public int read(byte[] b) throws IOException {
-        int r = in.read(b);
-        mCount += (r >= 0) ? r : 0;
-        return r;
-    }
-
-    @Override
-    public int read(byte[] b, int off, int len) throws IOException {
-        int r = in.read(b, off, len);
-        mCount += (r >= 0) ? r : 0;
-        return r;
-    }
-
-    @Override
-    public int read() throws IOException {
-        int r = in.read();
-        mCount += (r >= 0) ? 1 : 0;
-        return r;
-    }
-
-    @Override
-    public long skip(long length) throws IOException {
-        long skip = in.skip(length);
-        mCount += skip;
-        return skip;
-    }
-
-    public void skipOrThrow(long length) throws IOException {
-        if (skip(length) != length) throw new EOFException();
-    }
-
-    public void skipTo(long target) throws IOException {
-        long cur = mCount;
-        long diff = target - cur;
-        assert(diff >= 0);
-        skipOrThrow(diff);
-    }
-
-    public void readOrThrow(byte[] b, int off, int len) throws IOException {
-        int r = read(b, off, len);
-        if (r != len) throw new EOFException();
-    }
-
-    public void readOrThrow(byte[] b) throws IOException {
-        readOrThrow(b, 0, b.length);
-    }
-
-    public void setByteOrder(ByteOrder order) {
-        mByteBuffer.order(order);
-    }
-
-    public ByteOrder getByteOrder() {
-        return mByteBuffer.order();
-    }
-
-    public short readShort() throws IOException {
-        readOrThrow(mByteArray, 0 ,2);
-        mByteBuffer.rewind();
-        return mByteBuffer.getShort();
-    }
-
-    public int readUnsignedShort() throws IOException {
-        return readShort() & 0xffff;
-    }
-
-    public int readInt() throws IOException {
-        readOrThrow(mByteArray, 0 , 4);
-        mByteBuffer.rewind();
-        return mByteBuffer.getInt();
-    }
-
-    public long readUnsignedInt() throws IOException {
-        return readInt() & 0xffffffffL;
-    }
-
-    public long readLong() throws IOException {
-        readOrThrow(mByteArray, 0 , 8);
-        mByteBuffer.rewind();
-        return mByteBuffer.getLong();
-    }
-
-    public String readString(int n) throws IOException {
-        byte buf[] = new byte[n];
-        readOrThrow(buf);
-        return new String(buf, "UTF8");
-    }
-
-    public String readString(int n, Charset charset) throws IOException {
-        byte buf[] = new byte[n];
-        readOrThrow(buf);
-        return new String(buf, charset);
-    }
-}
\ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifData.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifData.java
deleted file mode 100644
index 8422382..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifData.java
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-import android.util.Log;
-
-import java.io.UnsupportedEncodingException;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * This class stores the EXIF header in IFDs according to the JPEG
- * specification. It is the result produced by {@link ExifReader}.
- *
- * @see ExifReader
- * @see IfdData
- */
-class ExifData {
-    private static final String TAG = "ExifData";
-    private static final byte[] USER_COMMENT_ASCII = {
-            0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00
-    };
-    private static final byte[] USER_COMMENT_JIS = {
-            0x4A, 0x49, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00
-    };
-    private static final byte[] USER_COMMENT_UNICODE = {
-            0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x00
-    };
-
-    private final IfdData[] mIfdDatas = new IfdData[IfdId.TYPE_IFD_COUNT];
-    private byte[] mThumbnail;
-    private ArrayList<byte[]> mStripBytes = new ArrayList<byte[]>();
-    private final ByteOrder mByteOrder;
-
-    ExifData(ByteOrder order) {
-        mByteOrder = order;
-    }
-
-    /**
-     * Gets the compressed thumbnail. Returns null if there is no compressed
-     * thumbnail.
-     *
-     * @see #hasCompressedThumbnail()
-     */
-    protected byte[] getCompressedThumbnail() {
-        return mThumbnail;
-    }
-
-    /**
-     * Sets the compressed thumbnail.
-     */
-    protected void setCompressedThumbnail(byte[] thumbnail) {
-        mThumbnail = thumbnail;
-    }
-
-    /**
-     * Returns true it this header contains a compressed thumbnail.
-     */
-    protected boolean hasCompressedThumbnail() {
-        return mThumbnail != null;
-    }
-
-    /**
-     * Adds an uncompressed strip.
-     */
-    protected void setStripBytes(int index, byte[] strip) {
-        if (index < mStripBytes.size()) {
-            mStripBytes.set(index, strip);
-        } else {
-            for (int i = mStripBytes.size(); i < index; i++) {
-                mStripBytes.add(null);
-            }
-            mStripBytes.add(strip);
-        }
-    }
-
-    /**
-     * Gets the strip count.
-     */
-    protected int getStripCount() {
-        return mStripBytes.size();
-    }
-
-    /**
-     * Gets the strip at the specified index.
-     *
-     * @exceptions #IndexOutOfBoundException
-     */
-    protected byte[] getStrip(int index) {
-        return mStripBytes.get(index);
-    }
-
-    /**
-     * Returns true if this header contains uncompressed strip.
-     */
-    protected boolean hasUncompressedStrip() {
-        return mStripBytes.size() != 0;
-    }
-
-    /**
-     * Gets the byte order.
-     */
-    protected ByteOrder getByteOrder() {
-        return mByteOrder;
-    }
-
-    /**
-     * Returns the {@link IfdData} object corresponding to a given IFD if it
-     * exists or null.
-     */
-    protected IfdData getIfdData(int ifdId) {
-        if (ExifTag.isValidIfd(ifdId)) {
-            return mIfdDatas[ifdId];
-        }
-        return null;
-    }
-
-    /**
-     * Adds IFD data. If IFD data of the same type already exists, it will be
-     * replaced by the new data.
-     */
-    protected void addIfdData(IfdData data) {
-        mIfdDatas[data.getId()] = data;
-    }
-
-    /**
-     * Returns the {@link IfdData} object corresponding to a given IFD or
-     * generates one if none exist.
-     */
-    protected IfdData getOrCreateIfdData(int ifdId) {
-        IfdData ifdData = mIfdDatas[ifdId];
-        if (ifdData == null) {
-            ifdData = new IfdData(ifdId);
-            mIfdDatas[ifdId] = ifdData;
-        }
-        return ifdData;
-    }
-
-    /**
-     * Returns the tag with a given TID in the given IFD if the tag exists.
-     * Otherwise returns null.
-     */
-    protected ExifTag getTag(short tag, int ifd) {
-        IfdData ifdData = mIfdDatas[ifd];
-        return (ifdData == null) ? null : ifdData.getTag(tag);
-    }
-
-    /**
-     * Adds the given ExifTag to its default IFD and returns an existing ExifTag
-     * with the same TID or null if none exist.
-     */
-    protected ExifTag addTag(ExifTag tag) {
-        if (tag != null) {
-            int ifd = tag.getIfd();
-            return addTag(tag, ifd);
-        }
-        return null;
-    }
-
-    /**
-     * Adds the given ExifTag to the given IFD and returns an existing ExifTag
-     * with the same TID or null if none exist.
-     */
-    protected ExifTag addTag(ExifTag tag, int ifdId) {
-        if (tag != null && ExifTag.isValidIfd(ifdId)) {
-            IfdData ifdData = getOrCreateIfdData(ifdId);
-            return ifdData.setTag(tag);
-        }
-        return null;
-    }
-
-    protected void clearThumbnailAndStrips() {
-        mThumbnail = null;
-        mStripBytes.clear();
-    }
-
-    /**
-     * Removes the thumbnail and its related tags. IFD1 will be removed.
-     */
-    protected void removeThumbnailData() {
-        clearThumbnailAndStrips();
-        mIfdDatas[IfdId.TYPE_IFD_1] = null;
-    }
-
-    /**
-     * Removes the tag with a given TID and IFD.
-     */
-    protected void removeTag(short tagId, int ifdId) {
-        IfdData ifdData = mIfdDatas[ifdId];
-        if (ifdData == null) {
-            return;
-        }
-        ifdData.removeTag(tagId);
-    }
-
-    /**
-     * Decodes the user comment tag into string as specified in the EXIF
-     * standard. Returns null if decoding failed.
-     */
-    protected String getUserComment() {
-        IfdData ifdData = mIfdDatas[IfdId.TYPE_IFD_0];
-        if (ifdData == null) {
-            return null;
-        }
-        ExifTag tag = ifdData.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_USER_COMMENT));
-        if (tag == null) {
-            return null;
-        }
-        if (tag.getComponentCount() < 8) {
-            return null;
-        }
-
-        byte[] buf = new byte[tag.getComponentCount()];
-        tag.getBytes(buf);
-
-        byte[] code = new byte[8];
-        System.arraycopy(buf, 0, code, 0, 8);
-
-        try {
-            if (Arrays.equals(code, USER_COMMENT_ASCII)) {
-                return new String(buf, 8, buf.length - 8, "US-ASCII");
-            } else if (Arrays.equals(code, USER_COMMENT_JIS)) {
-                return new String(buf, 8, buf.length - 8, "EUC-JP");
-            } else if (Arrays.equals(code, USER_COMMENT_UNICODE)) {
-                return new String(buf, 8, buf.length - 8, "UTF-16");
-            } else {
-                return null;
-            }
-        } catch (UnsupportedEncodingException e) {
-            Log.w(TAG, "Failed to decode the user comment");
-            return null;
-        }
-    }
-
-    /**
-     * Returns a list of all {@link ExifTag}s in the ExifData or null if there
-     * are none.
-     */
-    protected List<ExifTag> getAllTags() {
-        ArrayList<ExifTag> ret = new ArrayList<ExifTag>();
-        for (IfdData d : mIfdDatas) {
-            if (d != null) {
-                ExifTag[] tags = d.getAllTags();
-                if (tags != null) {
-                    for (ExifTag t : tags) {
-                        ret.add(t);
-                    }
-                }
-            }
-        }
-        if (ret.size() == 0) {
-            return null;
-        }
-        return ret;
-    }
-
-    /**
-     * Returns a list of all {@link ExifTag}s in a given IFD or null if there
-     * are none.
-     */
-    protected List<ExifTag> getAllTagsForIfd(int ifd) {
-        IfdData d = mIfdDatas[ifd];
-        if (d == null) {
-            return null;
-        }
-        ExifTag[] tags = d.getAllTags();
-        if (tags == null) {
-            return null;
-        }
-        ArrayList<ExifTag> ret = new ArrayList<ExifTag>(tags.length);
-        for (ExifTag t : tags) {
-            ret.add(t);
-        }
-        if (ret.size() == 0) {
-            return null;
-        }
-        return ret;
-    }
-
-    /**
-     * Returns a list of all {@link ExifTag}s with a given TID or null if there
-     * are none.
-     */
-    protected List<ExifTag> getAllTagsForTagId(short tag) {
-        ArrayList<ExifTag> ret = new ArrayList<ExifTag>();
-        for (IfdData d : mIfdDatas) {
-            if (d != null) {
-                ExifTag t = d.getTag(tag);
-                if (t != null) {
-                    ret.add(t);
-                }
-            }
-        }
-        if (ret.size() == 0) {
-            return null;
-        }
-        return ret;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (obj instanceof ExifData) {
-            ExifData data = (ExifData) obj;
-            if (data.mByteOrder != mByteOrder ||
-                    data.mStripBytes.size() != mStripBytes.size() ||
-                    !Arrays.equals(data.mThumbnail, mThumbnail)) {
-                return false;
-            }
-            for (int i = 0; i < mStripBytes.size(); i++) {
-                if (!Arrays.equals(data.mStripBytes.get(i), mStripBytes.get(i))) {
-                    return false;
-                }
-            }
-            for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
-                IfdData ifd1 = data.getIfdData(i);
-                IfdData ifd2 = getIfdData(i);
-                if (ifd1 != ifd2 && ifd1 != null && !ifd1.equals(ifd2)) {
-                    return false;
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifInterface.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifInterface.java
deleted file mode 100644
index 9247e87..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifInterface.java
+++ /dev/null
@@ -1,2407 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.util.SparseIntArray;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.channels.FileChannel.MapMode;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.TimeZone;
-
-/**
- * This class provides methods and constants for reading and writing jpeg file
- * metadata. It contains a collection of ExifTags, and a collection of
- * definitions for creating valid ExifTags. The collection of ExifTags can be
- * updated by: reading new ones from a file, deleting or adding existing ones,
- * or building new ExifTags from a tag definition. These ExifTags can be written
- * to a valid jpeg image as exif metadata.
- * <p>
- * Each ExifTag has a tag ID (TID) and is stored in a specific image file
- * directory (IFD) as specified by the exif standard. A tag definition can be
- * looked up with a constant that is a combination of TID and IFD. This
- * definition has information about the type, number of components, and valid
- * IFDs for a tag.
- *
- * @see ExifTag
- */
-public class ExifInterface {
-    public static final int TAG_NULL = -1;
-    public static final int IFD_NULL = -1;
-    public static final int DEFINITION_NULL = 0;
-
-    /**
-     * Tag constants for Jeita EXIF 2.2
-     */
-
-    // IFD 0
-    public static final int TAG_IMAGE_WIDTH =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0100);
-    public static final int TAG_IMAGE_LENGTH =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0101); // Image height
-    public static final int TAG_BITS_PER_SAMPLE =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0102);
-    public static final int TAG_COMPRESSION =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0103);
-    public static final int TAG_PHOTOMETRIC_INTERPRETATION =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0106);
-    public static final int TAG_IMAGE_DESCRIPTION =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x010E);
-    public static final int TAG_MAKE =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x010F);
-    public static final int TAG_MODEL =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0110);
-    public static final int TAG_STRIP_OFFSETS =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0111);
-    public static final int TAG_ORIENTATION =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0112);
-    public static final int TAG_SAMPLES_PER_PIXEL =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0115);
-    public static final int TAG_ROWS_PER_STRIP =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0116);
-    public static final int TAG_STRIP_BYTE_COUNTS =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0117);
-    public static final int TAG_X_RESOLUTION =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x011A);
-    public static final int TAG_Y_RESOLUTION =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x011B);
-    public static final int TAG_PLANAR_CONFIGURATION =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x011C);
-    public static final int TAG_RESOLUTION_UNIT =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0128);
-    public static final int TAG_TRANSFER_FUNCTION =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x012D);
-    public static final int TAG_SOFTWARE =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0131);
-    public static final int TAG_DATE_TIME =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0132);
-    public static final int TAG_ARTIST =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x013B);
-    public static final int TAG_WHITE_POINT =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x013E);
-    public static final int TAG_PRIMARY_CHROMATICITIES =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x013F);
-    public static final int TAG_Y_CB_CR_COEFFICIENTS =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0211);
-    public static final int TAG_Y_CB_CR_SUB_SAMPLING =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0212);
-    public static final int TAG_Y_CB_CR_POSITIONING =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0213);
-    public static final int TAG_REFERENCE_BLACK_WHITE =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x0214);
-    public static final int TAG_COPYRIGHT =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x8298);
-    public static final int TAG_EXIF_IFD =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x8769);
-    public static final int TAG_GPS_IFD =
-        defineTag(IfdId.TYPE_IFD_0, (short) 0x8825);
-    // IFD 1
-    public static final int TAG_JPEG_INTERCHANGE_FORMAT =
-        defineTag(IfdId.TYPE_IFD_1, (short) 0x0201);
-    public static final int TAG_JPEG_INTERCHANGE_FORMAT_LENGTH =
-        defineTag(IfdId.TYPE_IFD_1, (short) 0x0202);
-    // IFD Exif Tags
-    public static final int TAG_EXPOSURE_TIME =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x829A);
-    public static final int TAG_F_NUMBER =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x829D);
-    public static final int TAG_EXPOSURE_PROGRAM =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8822);
-    public static final int TAG_SPECTRAL_SENSITIVITY =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8824);
-    public static final int TAG_ISO_SPEED_RATINGS =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8827);
-    public static final int TAG_OECF =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x8828);
-    public static final int TAG_EXIF_VERSION =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9000);
-    public static final int TAG_DATE_TIME_ORIGINAL =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9003);
-    public static final int TAG_DATE_TIME_DIGITIZED =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9004);
-    public static final int TAG_COMPONENTS_CONFIGURATION =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9101);
-    public static final int TAG_COMPRESSED_BITS_PER_PIXEL =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9102);
-    public static final int TAG_SHUTTER_SPEED_VALUE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9201);
-    public static final int TAG_APERTURE_VALUE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9202);
-    public static final int TAG_BRIGHTNESS_VALUE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9203);
-    public static final int TAG_EXPOSURE_BIAS_VALUE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9204);
-    public static final int TAG_MAX_APERTURE_VALUE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9205);
-    public static final int TAG_SUBJECT_DISTANCE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9206);
-    public static final int TAG_METERING_MODE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9207);
-    public static final int TAG_LIGHT_SOURCE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9208);
-    public static final int TAG_FLASH =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9209);
-    public static final int TAG_FOCAL_LENGTH =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x920A);
-    public static final int TAG_SUBJECT_AREA =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9214);
-    public static final int TAG_MAKER_NOTE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x927C);
-    public static final int TAG_USER_COMMENT =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9286);
-    public static final int TAG_SUB_SEC_TIME =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9290);
-    public static final int TAG_SUB_SEC_TIME_ORIGINAL =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9291);
-    public static final int TAG_SUB_SEC_TIME_DIGITIZED =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0x9292);
-    public static final int TAG_FLASHPIX_VERSION =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA000);
-    public static final int TAG_COLOR_SPACE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA001);
-    public static final int TAG_PIXEL_X_DIMENSION =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA002);
-    public static final int TAG_PIXEL_Y_DIMENSION =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA003);
-    public static final int TAG_RELATED_SOUND_FILE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA004);
-    public static final int TAG_INTEROPERABILITY_IFD =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA005);
-    public static final int TAG_FLASH_ENERGY =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20B);
-    public static final int TAG_SPATIAL_FREQUENCY_RESPONSE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20C);
-    public static final int TAG_FOCAL_PLANE_X_RESOLUTION =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20E);
-    public static final int TAG_FOCAL_PLANE_Y_RESOLUTION =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA20F);
-    public static final int TAG_FOCAL_PLANE_RESOLUTION_UNIT =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA210);
-    public static final int TAG_SUBJECT_LOCATION =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA214);
-    public static final int TAG_EXPOSURE_INDEX =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA215);
-    public static final int TAG_SENSING_METHOD =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA217);
-    public static final int TAG_FILE_SOURCE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA300);
-    public static final int TAG_SCENE_TYPE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA301);
-    public static final int TAG_CFA_PATTERN =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA302);
-    public static final int TAG_CUSTOM_RENDERED =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA401);
-    public static final int TAG_EXPOSURE_MODE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA402);
-    public static final int TAG_WHITE_BALANCE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA403);
-    public static final int TAG_DIGITAL_ZOOM_RATIO =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA404);
-    public static final int TAG_FOCAL_LENGTH_IN_35_MM_FILE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA405);
-    public static final int TAG_SCENE_CAPTURE_TYPE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA406);
-    public static final int TAG_GAIN_CONTROL =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA407);
-    public static final int TAG_CONTRAST =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA408);
-    public static final int TAG_SATURATION =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA409);
-    public static final int TAG_SHARPNESS =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA40A);
-    public static final int TAG_DEVICE_SETTING_DESCRIPTION =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA40B);
-    public static final int TAG_SUBJECT_DISTANCE_RANGE =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA40C);
-    public static final int TAG_IMAGE_UNIQUE_ID =
-        defineTag(IfdId.TYPE_IFD_EXIF, (short) 0xA420);
-    // IFD GPS tags
-    public static final int TAG_GPS_VERSION_ID =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 0);
-    public static final int TAG_GPS_LATITUDE_REF =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 1);
-    public static final int TAG_GPS_LATITUDE =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 2);
-    public static final int TAG_GPS_LONGITUDE_REF =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 3);
-    public static final int TAG_GPS_LONGITUDE =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 4);
-    public static final int TAG_GPS_ALTITUDE_REF =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 5);
-    public static final int TAG_GPS_ALTITUDE =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 6);
-    public static final int TAG_GPS_TIME_STAMP =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 7);
-    public static final int TAG_GPS_SATTELLITES =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 8);
-    public static final int TAG_GPS_STATUS =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 9);
-    public static final int TAG_GPS_MEASURE_MODE =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 10);
-    public static final int TAG_GPS_DOP =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 11);
-    public static final int TAG_GPS_SPEED_REF =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 12);
-    public static final int TAG_GPS_SPEED =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 13);
-    public static final int TAG_GPS_TRACK_REF =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 14);
-    public static final int TAG_GPS_TRACK =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 15);
-    public static final int TAG_GPS_IMG_DIRECTION_REF =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 16);
-    public static final int TAG_GPS_IMG_DIRECTION =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 17);
-    public static final int TAG_GPS_MAP_DATUM =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 18);
-    public static final int TAG_GPS_DEST_LATITUDE_REF =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 19);
-    public static final int TAG_GPS_DEST_LATITUDE =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 20);
-    public static final int TAG_GPS_DEST_LONGITUDE_REF =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 21);
-    public static final int TAG_GPS_DEST_LONGITUDE =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 22);
-    public static final int TAG_GPS_DEST_BEARING_REF =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 23);
-    public static final int TAG_GPS_DEST_BEARING =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 24);
-    public static final int TAG_GPS_DEST_DISTANCE_REF =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 25);
-    public static final int TAG_GPS_DEST_DISTANCE =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 26);
-    public static final int TAG_GPS_PROCESSING_METHOD =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 27);
-    public static final int TAG_GPS_AREA_INFORMATION =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 28);
-    public static final int TAG_GPS_DATE_STAMP =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 29);
-    public static final int TAG_GPS_DIFFERENTIAL =
-        defineTag(IfdId.TYPE_IFD_GPS, (short) 30);
-    // IFD Interoperability tags
-    public static final int TAG_INTEROPERABILITY_INDEX =
-        defineTag(IfdId.TYPE_IFD_INTEROPERABILITY, (short) 1);
-
-    /**
-     * Tags that contain offset markers. These are included in the banned
-     * defines.
-     */
-    private static HashSet<Short> sOffsetTags = new HashSet<Short>();
-    static {
-        sOffsetTags.add(getTrueTagKey(TAG_GPS_IFD));
-        sOffsetTags.add(getTrueTagKey(TAG_EXIF_IFD));
-        sOffsetTags.add(getTrueTagKey(TAG_JPEG_INTERCHANGE_FORMAT));
-        sOffsetTags.add(getTrueTagKey(TAG_INTEROPERABILITY_IFD));
-        sOffsetTags.add(getTrueTagKey(TAG_STRIP_OFFSETS));
-    }
-
-    /**
-     * Tags with definitions that cannot be overridden (banned defines).
-     */
-    protected static HashSet<Short> sBannedDefines = new HashSet<Short>(sOffsetTags);
-    static {
-        sBannedDefines.add(getTrueTagKey(TAG_NULL));
-        sBannedDefines.add(getTrueTagKey(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH));
-        sBannedDefines.add(getTrueTagKey(TAG_STRIP_BYTE_COUNTS));
-    }
-
-    /**
-     * Returns the constant representing a tag with a given TID and default IFD.
-     */
-    public static int defineTag(int ifdId, short tagId) {
-        return (tagId & 0x0000ffff) | (ifdId << 16);
-    }
-
-    /**
-     * Returns the TID for a tag constant.
-     */
-    public static short getTrueTagKey(int tag) {
-        // Truncate
-        return (short) tag;
-    }
-
-    /**
-     * Returns the default IFD for a tag constant.
-     */
-    public static int getTrueIfd(int tag) {
-        return tag >>> 16;
-    }
-
-    /**
-     * Constants for {@link TAG_ORIENTATION}. They can be interpreted as
-     * follows:
-     * <ul>
-     * <li>TOP_LEFT is the normal orientation.</li>
-     * <li>TOP_RIGHT is a left-right mirror.</li>
-     * <li>BOTTOM_LEFT is a 180 degree rotation.</li>
-     * <li>BOTTOM_RIGHT is a top-bottom mirror.</li>
-     * <li>LEFT_TOP is mirrored about the top-left<->bottom-right axis.</li>
-     * <li>RIGHT_TOP is a 90 degree clockwise rotation.</li>
-     * <li>LEFT_BOTTOM is mirrored about the top-right<->bottom-left axis.</li>
-     * <li>RIGHT_BOTTOM is a 270 degree clockwise rotation.</li>
-     * </ul>
-     */
-    public static interface Orientation {
-        public static final short TOP_LEFT = 1;
-        public static final short TOP_RIGHT = 2;
-        public static final short BOTTOM_LEFT = 3;
-        public static final short BOTTOM_RIGHT = 4;
-        public static final short LEFT_TOP = 5;
-        public static final short RIGHT_TOP = 6;
-        public static final short LEFT_BOTTOM = 7;
-        public static final short RIGHT_BOTTOM = 8;
-    }
-
-    /**
-     * Constants for {@link TAG_Y_CB_CR_POSITIONING}
-     */
-    public static interface YCbCrPositioning {
-        public static final short CENTERED = 1;
-        public static final short CO_SITED = 2;
-    }
-
-    /**
-     * Constants for {@link TAG_COMPRESSION}
-     */
-    public static interface Compression {
-        public static final short UNCOMPRESSION = 1;
-        public static final short JPEG = 6;
-    }
-
-    /**
-     * Constants for {@link TAG_RESOLUTION_UNIT}
-     */
-    public static interface ResolutionUnit {
-        public static final short INCHES = 2;
-        public static final short CENTIMETERS = 3;
-    }
-
-    /**
-     * Constants for {@link TAG_PHOTOMETRIC_INTERPRETATION}
-     */
-    public static interface PhotometricInterpretation {
-        public static final short RGB = 2;
-        public static final short YCBCR = 6;
-    }
-
-    /**
-     * Constants for {@link TAG_PLANAR_CONFIGURATION}
-     */
-    public static interface PlanarConfiguration {
-        public static final short CHUNKY = 1;
-        public static final short PLANAR = 2;
-    }
-
-    /**
-     * Constants for {@link TAG_EXPOSURE_PROGRAM}
-     */
-    public static interface ExposureProgram {
-        public static final short NOT_DEFINED = 0;
-        public static final short MANUAL = 1;
-        public static final short NORMAL_PROGRAM = 2;
-        public static final short APERTURE_PRIORITY = 3;
-        public static final short SHUTTER_PRIORITY = 4;
-        public static final short CREATIVE_PROGRAM = 5;
-        public static final short ACTION_PROGRAM = 6;
-        public static final short PROTRAIT_MODE = 7;
-        public static final short LANDSCAPE_MODE = 8;
-    }
-
-    /**
-     * Constants for {@link TAG_METERING_MODE}
-     */
-    public static interface MeteringMode {
-        public static final short UNKNOWN = 0;
-        public static final short AVERAGE = 1;
-        public static final short CENTER_WEIGHTED_AVERAGE = 2;
-        public static final short SPOT = 3;
-        public static final short MULTISPOT = 4;
-        public static final short PATTERN = 5;
-        public static final short PARTAIL = 6;
-        public static final short OTHER = 255;
-    }
-
-    /**
-     * Constants for {@link TAG_FLASH} As the definition in Jeita EXIF 2.2
-     * standard, we can treat this constant as bitwise flag.
-     * <p>
-     * e.g.
-     * <p>
-     * short flash = FIRED | RETURN_STROBE_RETURN_LIGHT_DETECTED |
-     * MODE_AUTO_MODE
-     */
-    public static interface Flash {
-        // LSB
-        public static final short DID_NOT_FIRED = 0;
-        public static final short FIRED = 1;
-        // 1st~2nd bits
-        public static final short RETURN_NO_STROBE_RETURN_DETECTION_FUNCTION = 0 << 1;
-        public static final short RETURN_STROBE_RETURN_LIGHT_NOT_DETECTED = 2 << 1;
-        public static final short RETURN_STROBE_RETURN_LIGHT_DETECTED = 3 << 1;
-        // 3rd~4th bits
-        public static final short MODE_UNKNOWN = 0 << 3;
-        public static final short MODE_COMPULSORY_FLASH_FIRING = 1 << 3;
-        public static final short MODE_COMPULSORY_FLASH_SUPPRESSION = 2 << 3;
-        public static final short MODE_AUTO_MODE = 3 << 3;
-        // 5th bit
-        public static final short FUNCTION_PRESENT = 0 << 5;
-        public static final short FUNCTION_NO_FUNCTION = 1 << 5;
-        // 6th bit
-        public static final short RED_EYE_REDUCTION_NO_OR_UNKNOWN = 0 << 6;
-        public static final short RED_EYE_REDUCTION_SUPPORT = 1 << 6;
-    }
-
-    /**
-     * Constants for {@link TAG_COLOR_SPACE}
-     */
-    public static interface ColorSpace {
-        public static final short SRGB = 1;
-        public static final short UNCALIBRATED = (short) 0xFFFF;
-    }
-
-    /**
-     * Constants for {@link TAG_EXPOSURE_MODE}
-     */
-    public static interface ExposureMode {
-        public static final short AUTO_EXPOSURE = 0;
-        public static final short MANUAL_EXPOSURE = 1;
-        public static final short AUTO_BRACKET = 2;
-    }
-
-    /**
-     * Constants for {@link TAG_WHITE_BALANCE}
-     */
-    public static interface WhiteBalance {
-        public static final short AUTO = 0;
-        public static final short MANUAL = 1;
-    }
-
-    /**
-     * Constants for {@link TAG_SCENE_CAPTURE_TYPE}
-     */
-    public static interface SceneCapture {
-        public static final short STANDARD = 0;
-        public static final short LANDSCAPE = 1;
-        public static final short PROTRAIT = 2;
-        public static final short NIGHT_SCENE = 3;
-    }
-
-    /**
-     * Constants for {@link TAG_COMPONENTS_CONFIGURATION}
-     */
-    public static interface ComponentsConfiguration {
-        public static final short NOT_EXIST = 0;
-        public static final short Y = 1;
-        public static final short CB = 2;
-        public static final short CR = 3;
-        public static final short R = 4;
-        public static final short G = 5;
-        public static final short B = 6;
-    }
-
-    /**
-     * Constants for {@link TAG_LIGHT_SOURCE}
-     */
-    public static interface LightSource {
-        public static final short UNKNOWN = 0;
-        public static final short DAYLIGHT = 1;
-        public static final short FLUORESCENT = 2;
-        public static final short TUNGSTEN = 3;
-        public static final short FLASH = 4;
-        public static final short FINE_WEATHER = 9;
-        public static final short CLOUDY_WEATHER = 10;
-        public static final short SHADE = 11;
-        public static final short DAYLIGHT_FLUORESCENT = 12;
-        public static final short DAY_WHITE_FLUORESCENT = 13;
-        public static final short COOL_WHITE_FLUORESCENT = 14;
-        public static final short WHITE_FLUORESCENT = 15;
-        public static final short STANDARD_LIGHT_A = 17;
-        public static final short STANDARD_LIGHT_B = 18;
-        public static final short STANDARD_LIGHT_C = 19;
-        public static final short D55 = 20;
-        public static final short D65 = 21;
-        public static final short D75 = 22;
-        public static final short D50 = 23;
-        public static final short ISO_STUDIO_TUNGSTEN = 24;
-        public static final short OTHER = 255;
-    }
-
-    /**
-     * Constants for {@link TAG_SENSING_METHOD}
-     */
-    public static interface SensingMethod {
-        public static final short NOT_DEFINED = 1;
-        public static final short ONE_CHIP_COLOR = 2;
-        public static final short TWO_CHIP_COLOR = 3;
-        public static final short THREE_CHIP_COLOR = 4;
-        public static final short COLOR_SEQUENTIAL_AREA = 5;
-        public static final short TRILINEAR = 7;
-        public static final short COLOR_SEQUENTIAL_LINEAR = 8;
-    }
-
-    /**
-     * Constants for {@link TAG_FILE_SOURCE}
-     */
-    public static interface FileSource {
-        public static final short DSC = 3;
-    }
-
-    /**
-     * Constants for {@link TAG_SCENE_TYPE}
-     */
-    public static interface SceneType {
-        public static final short DIRECT_PHOTOGRAPHED = 1;
-    }
-
-    /**
-     * Constants for {@link TAG_GAIN_CONTROL}
-     */
-    public static interface GainControl {
-        public static final short NONE = 0;
-        public static final short LOW_UP = 1;
-        public static final short HIGH_UP = 2;
-        public static final short LOW_DOWN = 3;
-        public static final short HIGH_DOWN = 4;
-    }
-
-    /**
-     * Constants for {@link TAG_CONTRAST}
-     */
-    public static interface Contrast {
-        public static final short NORMAL = 0;
-        public static final short SOFT = 1;
-        public static final short HARD = 2;
-    }
-
-    /**
-     * Constants for {@link TAG_SATURATION}
-     */
-    public static interface Saturation {
-        public static final short NORMAL = 0;
-        public static final short LOW = 1;
-        public static final short HIGH = 2;
-    }
-
-    /**
-     * Constants for {@link TAG_SHARPNESS}
-     */
-    public static interface Sharpness {
-        public static final short NORMAL = 0;
-        public static final short SOFT = 1;
-        public static final short HARD = 2;
-    }
-
-    /**
-     * Constants for {@link TAG_SUBJECT_DISTANCE}
-     */
-    public static interface SubjectDistance {
-        public static final short UNKNOWN = 0;
-        public static final short MACRO = 1;
-        public static final short CLOSE_VIEW = 2;
-        public static final short DISTANT_VIEW = 3;
-    }
-
-    /**
-     * Constants for {@link TAG_GPS_LATITUDE_REF},
-     * {@link TAG_GPS_DEST_LATITUDE_REF}
-     */
-    public static interface GpsLatitudeRef {
-        public static final String NORTH = "N";
-        public static final String SOUTH = "S";
-    }
-
-    /**
-     * Constants for {@link TAG_GPS_LONGITUDE_REF},
-     * {@link TAG_GPS_DEST_LONGITUDE_REF}
-     */
-    public static interface GpsLongitudeRef {
-        public static final String EAST = "E";
-        public static final String WEST = "W";
-    }
-
-    /**
-     * Constants for {@link TAG_GPS_ALTITUDE_REF}
-     */
-    public static interface GpsAltitudeRef {
-        public static final short SEA_LEVEL = 0;
-        public static final short SEA_LEVEL_NEGATIVE = 1;
-    }
-
-    /**
-     * Constants for {@link TAG_GPS_STATUS}
-     */
-    public static interface GpsStatus {
-        public static final String IN_PROGRESS = "A";
-        public static final String INTEROPERABILITY = "V";
-    }
-
-    /**
-     * Constants for {@link TAG_GPS_MEASURE_MODE}
-     */
-    public static interface GpsMeasureMode {
-        public static final String MODE_2_DIMENSIONAL = "2";
-        public static final String MODE_3_DIMENSIONAL = "3";
-    }
-
-    /**
-     * Constants for {@link TAG_GPS_SPEED_REF},
-     * {@link TAG_GPS_DEST_DISTANCE_REF}
-     */
-    public static interface GpsSpeedRef {
-        public static final String KILOMETERS = "K";
-        public static final String MILES = "M";
-        public static final String KNOTS = "N";
-    }
-
-    /**
-     * Constants for {@link TAG_GPS_TRACK_REF},
-     * {@link TAG_GPS_IMG_DIRECTION_REF}, {@link TAG_GPS_DEST_BEARING_REF}
-     */
-    public static interface GpsTrackRef {
-        public static final String TRUE_DIRECTION = "T";
-        public static final String MAGNETIC_DIRECTION = "M";
-    }
-
-    /**
-     * Constants for {@link TAG_GPS_DIFFERENTIAL}
-     */
-    public static interface GpsDifferential {
-        public static final short WITHOUT_DIFFERENTIAL_CORRECTION = 0;
-        public static final short DIFFERENTIAL_CORRECTION_APPLIED = 1;
-    }
-
-    private static final String NULL_ARGUMENT_STRING = "Argument is null";
-    private ExifData mData = new ExifData(DEFAULT_BYTE_ORDER);
-    public static final ByteOrder DEFAULT_BYTE_ORDER = ByteOrder.BIG_ENDIAN;
-
-    public ExifInterface() {
-        mGPSDateStampFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
-    }
-
-    /**
-     * Reads the exif tags from a byte array, clearing this ExifInterface
-     * object's existing exif tags.
-     *
-     * @param jpeg a byte array containing a jpeg compressed image.
-     * @throws IOException
-     */
-    public void readExif(byte[] jpeg) throws IOException {
-        readExif(new ByteArrayInputStream(jpeg));
-    }
-
-    /**
-     * Reads the exif tags from an InputStream, clearing this ExifInterface
-     * object's existing exif tags.
-     *
-     * @param inStream an InputStream containing a jpeg compressed image.
-     * @throws IOException
-     */
-    public void readExif(InputStream inStream) throws IOException {
-        if (inStream == null) {
-            throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
-        }
-        ExifData d = null;
-        try {
-            d = new ExifReader(this).read(inStream);
-        } catch (ExifInvalidFormatException e) {
-            throw new IOException("Invalid exif format : " + e);
-        }
-        mData = d;
-    }
-
-    /**
-     * Reads the exif tags from a file, clearing this ExifInterface object's
-     * existing exif tags.
-     *
-     * @param inFileName a string representing the filepath to jpeg file.
-     * @throws FileNotFoundException
-     * @throws IOException
-     */
-    public void readExif(String inFileName) throws FileNotFoundException, IOException {
-        if (inFileName == null) {
-            throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
-        }
-        InputStream is = null;
-        try {
-            is = (InputStream) new BufferedInputStream(new FileInputStream(inFileName));
-            readExif(is);
-        } catch (IOException e) {
-            closeSilently(is);
-            throw e;
-        }
-        is.close();
-    }
-
-    /**
-     * Sets the exif tags, clearing this ExifInterface object's existing exif
-     * tags.
-     *
-     * @param tags a collection of exif tags to set.
-     */
-    public void setExif(Collection<ExifTag> tags) {
-        clearExif();
-        setTags(tags);
-    }
-
-    /**
-     * Clears this ExifInterface object's existing exif tags.
-     */
-    public void clearExif() {
-        mData = new ExifData(DEFAULT_BYTE_ORDER);
-    }
-
-    /**
-     * Writes the tags from this ExifInterface object into a jpeg image,
-     * removing prior exif tags.
-     *
-     * @param jpeg a byte array containing a jpeg compressed image.
-     * @param exifOutStream an OutputStream to which the jpeg image with added
-     *            exif tags will be written.
-     * @throws IOException
-     */
-    public void writeExif(byte[] jpeg, OutputStream exifOutStream) throws IOException {
-        if (jpeg == null || exifOutStream == null) {
-            throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
-        }
-        OutputStream s = getExifWriterStream(exifOutStream);
-        s.write(jpeg, 0, jpeg.length);
-        s.flush();
-    }
-
-    /**
-     * Writes the tags from this ExifInterface object into a jpeg compressed
-     * bitmap, removing prior exif tags.
-     *
-     * @param bmap a bitmap to compress and write exif into.
-     * @param exifOutStream the OutputStream to which the jpeg image with added
-     *            exif tags will be written.
-     * @throws IOException
-     */
-    public void writeExif(Bitmap bmap, OutputStream exifOutStream) throws IOException {
-        if (bmap == null || exifOutStream == null) {
-            throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
-        }
-        OutputStream s = getExifWriterStream(exifOutStream);
-        bmap.compress(Bitmap.CompressFormat.JPEG, 90, s);
-        s.flush();
-    }
-
-    /**
-     * Writes the tags from this ExifInterface object into a jpeg stream,
-     * removing prior exif tags.
-     *
-     * @param jpegStream an InputStream containing a jpeg compressed image.
-     * @param exifOutStream an OutputStream to which the jpeg image with added
-     *            exif tags will be written.
-     * @throws IOException
-     */
-    public void writeExif(InputStream jpegStream, OutputStream exifOutStream) throws IOException {
-        if (jpegStream == null || exifOutStream == null) {
-            throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
-        }
-        OutputStream s = getExifWriterStream(exifOutStream);
-        doExifStreamIO(jpegStream, s);
-        s.flush();
-    }
-
-    /**
-     * Writes the tags from this ExifInterface object into a jpeg image,
-     * removing prior exif tags.
-     *
-     * @param jpeg a byte array containing a jpeg compressed image.
-     * @param exifOutFileName a String containing the filepath to which the jpeg
-     *            image with added exif tags will be written.
-     * @throws FileNotFoundException
-     * @throws IOException
-     */
-    public void writeExif(byte[] jpeg, String exifOutFileName) throws FileNotFoundException,
-            IOException {
-        if (jpeg == null || exifOutFileName == null) {
-            throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
-        }
-        OutputStream s = null;
-        try {
-            s = getExifWriterStream(exifOutFileName);
-            s.write(jpeg, 0, jpeg.length);
-            s.flush();
-        } catch (IOException e) {
-            closeSilently(s);
-            throw e;
-        }
-        s.close();
-    }
-
-    /**
-     * Writes the tags from this ExifInterface object into a jpeg compressed
-     * bitmap, removing prior exif tags.
-     *
-     * @param bmap a bitmap to compress and write exif into.
-     * @param exifOutFileName a String containing the filepath to which the jpeg
-     *            image with added exif tags will be written.
-     * @throws FileNotFoundException
-     * @throws IOException
-     */
-    public void writeExif(Bitmap bmap, String exifOutFileName) throws FileNotFoundException,
-            IOException {
-        if (bmap == null || exifOutFileName == null) {
-            throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
-        }
-        OutputStream s = null;
-        try {
-            s = getExifWriterStream(exifOutFileName);
-            bmap.compress(Bitmap.CompressFormat.JPEG, 90, s);
-            s.flush();
-        } catch (IOException e) {
-            closeSilently(s);
-            throw e;
-        }
-        s.close();
-    }
-
-    /**
-     * Writes the tags from this ExifInterface object into a jpeg stream,
-     * removing prior exif tags.
-     *
-     * @param jpegStream an InputStream containing a jpeg compressed image.
-     * @param exifOutFileName a String containing the filepath to which the jpeg
-     *            image with added exif tags will be written.
-     * @throws FileNotFoundException
-     * @throws IOException
-     */
-    public void writeExif(InputStream jpegStream, String exifOutFileName)
-            throws FileNotFoundException, IOException {
-        if (jpegStream == null || exifOutFileName == null) {
-            throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
-        }
-        OutputStream s = null;
-        try {
-            s = getExifWriterStream(exifOutFileName);
-            doExifStreamIO(jpegStream, s);
-            s.flush();
-        } catch (IOException e) {
-            closeSilently(s);
-            throw e;
-        }
-        s.close();
-    }
-
-    /**
-     * Writes the tags from this ExifInterface object into a jpeg file, removing
-     * prior exif tags.
-     *
-     * @param jpegFileName a String containing the filepath for a jpeg file.
-     * @param exifOutFileName a String containing the filepath to which the jpeg
-     *            image with added exif tags will be written.
-     * @throws FileNotFoundException
-     * @throws IOException
-     */
-    public void writeExif(String jpegFileName, String exifOutFileName)
-            throws FileNotFoundException, IOException {
-        if (jpegFileName == null || exifOutFileName == null) {
-            throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
-        }
-        InputStream is = null;
-        try {
-            is = new FileInputStream(jpegFileName);
-            writeExif(is, exifOutFileName);
-        } catch (IOException e) {
-            closeSilently(is);
-            throw e;
-        }
-        is.close();
-    }
-
-    /**
-     * Wraps an OutputStream object with an ExifOutputStream. Exif tags in this
-     * ExifInterface object will be added to a jpeg image written to this
-     * stream, removing prior exif tags. Other methods of this ExifInterface
-     * object should not be called until the returned OutputStream has been
-     * closed.
-     *
-     * @param outStream an OutputStream to wrap.
-     * @return an OutputStream that wraps the outStream parameter, and adds exif
-     *         metadata. A jpeg image should be written to this stream.
-     */
-    public OutputStream getExifWriterStream(OutputStream outStream) {
-        if (outStream == null) {
-            throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
-        }
-        ExifOutputStream eos = new ExifOutputStream(outStream, this);
-        eos.setExifData(mData);
-        return eos;
-    }
-
-    /**
-     * Returns an OutputStream object that writes to a file. Exif tags in this
-     * ExifInterface object will be added to a jpeg image written to this
-     * stream, removing prior exif tags. Other methods of this ExifInterface
-     * object should not be called until the returned OutputStream has been
-     * closed.
-     *
-     * @param exifOutFileName an String containing a filepath for a jpeg file.
-     * @return an OutputStream that writes to the exifOutFileName file, and adds
-     *         exif metadata. A jpeg image should be written to this stream.
-     * @throws FileNotFoundException
-     */
-    public OutputStream getExifWriterStream(String exifOutFileName) throws FileNotFoundException {
-        if (exifOutFileName == null) {
-            throw new IllegalArgumentException(NULL_ARGUMENT_STRING);
-        }
-        OutputStream out = null;
-        try {
-            out = (OutputStream) new FileOutputStream(exifOutFileName);
-        } catch (FileNotFoundException e) {
-            closeSilently(out);
-            throw e;
-        }
-        return getExifWriterStream(out);
-    }
-
-    /**
-     * Attempts to do an in-place rewrite the exif metadata in a file for the
-     * given tags. If tags do not exist or do not have the same size as the
-     * existing exif tags, this method will fail.
-     *
-     * @param filename a String containing a filepath for a jpeg file with exif
-     *            tags to rewrite.
-     * @param tags tags that will be written into the jpeg file over existing
-     *            tags if possible.
-     * @return true if success, false if could not overwrite. If false, no
-     *         changes are made to the file.
-     * @throws FileNotFoundException
-     * @throws IOException
-     */
-    public boolean rewriteExif(String filename, Collection<ExifTag> tags)
-            throws FileNotFoundException, IOException {
-        RandomAccessFile file = null;
-        InputStream is = null;
-        boolean ret;
-        try {
-            File temp = new File(filename);
-            is = new BufferedInputStream(new FileInputStream(temp));
-
-            // Parse beginning of APP1 in exif to find size of exif header.
-            ExifParser parser = null;
-            try {
-                parser = ExifParser.parse(is, this);
-            } catch (ExifInvalidFormatException e) {
-                throw new IOException("Invalid exif format : ", e);
-            }
-            long exifSize = parser.getOffsetToExifEndFromSOF();
-
-            // Free up resources
-            is.close();
-            is = null;
-
-            // Open file for memory mapping.
-            file = new RandomAccessFile(temp, "rw");
-            long fileLength = file.length();
-            if (fileLength < exifSize) {
-                throw new IOException("Filesize changed during operation");
-            }
-
-            // Map only exif header into memory.
-            ByteBuffer buf = file.getChannel().map(MapMode.READ_WRITE, 0, exifSize);
-
-            // Attempt to overwrite tag values without changing lengths (avoids
-            // file copy).
-            ret = rewriteExif(buf, tags);
-        } catch (IOException e) {
-            closeSilently(file);
-            throw e;
-        } finally {
-            closeSilently(is);
-        }
-        file.close();
-        return ret;
-    }
-
-    /**
-     * Attempts to do an in-place rewrite the exif metadata in a ByteBuffer for
-     * the given tags. If tags do not exist or do not have the same size as the
-     * existing exif tags, this method will fail.
-     *
-     * @param buf a ByteBuffer containing a jpeg file with existing exif tags to
-     *            rewrite.
-     * @param tags tags that will be written into the jpeg ByteBuffer over
-     *            existing tags if possible.
-     * @return true if success, false if could not overwrite. If false, no
-     *         changes are made to the ByteBuffer.
-     * @throws IOException
-     */
-    public boolean rewriteExif(ByteBuffer buf, Collection<ExifTag> tags) throws IOException {
-        ExifModifier mod = null;
-        try {
-            mod = new ExifModifier(buf, this);
-            for (ExifTag t : tags) {
-                mod.modifyTag(t);
-            }
-            return mod.commit();
-        } catch (ExifInvalidFormatException e) {
-            throw new IOException("Invalid exif format : " + e);
-        }
-    }
-
-    /**
-     * Attempts to do an in-place rewrite of the exif metadata. If this fails,
-     * fall back to overwriting file. This preserves tags that are not being
-     * rewritten.
-     *
-     * @param filename a String containing a filepath for a jpeg file.
-     * @param tags tags that will be written into the jpeg file over existing
-     *            tags if possible.
-     * @throws FileNotFoundException
-     * @throws IOException
-     * @see #rewriteExif
-     */
-    public void forceRewriteExif(String filename, Collection<ExifTag> tags)
-            throws FileNotFoundException,
-            IOException {
-        // Attempt in-place write
-        if (!rewriteExif(filename, tags)) {
-            // Fall back to doing a copy
-            ExifData tempData = mData;
-            mData = new ExifData(DEFAULT_BYTE_ORDER);
-            FileInputStream is = null;
-            ByteArrayOutputStream bytes = null;
-            try {
-                is = new FileInputStream(filename);
-                bytes = new ByteArrayOutputStream();
-                doExifStreamIO(is, bytes);
-                byte[] imageBytes = bytes.toByteArray();
-                readExif(imageBytes);
-                setTags(tags);
-                writeExif(imageBytes, filename);
-            } catch (IOException e) {
-                closeSilently(is);
-                throw e;
-            } finally {
-                is.close();
-                // Prevent clobbering of mData
-                mData = tempData;
-            }
-        }
-    }
-
-    /**
-     * Attempts to do an in-place rewrite of the exif metadata using the tags in
-     * this ExifInterface object. If this fails, fall back to overwriting file.
-     * This preserves tags that are not being rewritten.
-     *
-     * @param filename a String containing a filepath for a jpeg file.
-     * @throws FileNotFoundException
-     * @throws IOException
-     * @see #rewriteExif
-     */
-    public void forceRewriteExif(String filename) throws FileNotFoundException, IOException {
-        forceRewriteExif(filename, getAllTags());
-    }
-
-    /**
-     * Get the exif tags in this ExifInterface object or null if none exist.
-     *
-     * @return a List of {@link ExifTag}s.
-     */
-    public List<ExifTag> getAllTags() {
-        return mData.getAllTags();
-    }
-
-    /**
-     * Returns a list of ExifTags that share a TID (which can be obtained by
-     * calling {@link #getTrueTagKey} on a defined tag constant) or null if none
-     * exist.
-     *
-     * @param tagId a TID as defined in the exif standard (or with
-     *            {@link #defineTag}).
-     * @return a List of {@link ExifTag}s.
-     */
-    public List<ExifTag> getTagsForTagId(short tagId) {
-        return mData.getAllTagsForTagId(tagId);
-    }
-
-    /**
-     * Returns a list of ExifTags that share an IFD (which can be obtained by
-     * calling {@link #getTrueIFD} on a defined tag constant) or null if none
-     * exist.
-     *
-     * @param ifdId an IFD as defined in the exif standard (or with
-     *            {@link #defineTag}).
-     * @return a List of {@link ExifTag}s.
-     */
-    public List<ExifTag> getTagsForIfdId(int ifdId) {
-        return mData.getAllTagsForIfd(ifdId);
-    }
-
-    /**
-     * Gets an ExifTag for an IFD other than the tag's default.
-     *
-     * @see #getTag
-     */
-    public ExifTag getTag(int tagId, int ifdId) {
-        if (!ExifTag.isValidIfd(ifdId)) {
-            return null;
-        }
-        return mData.getTag(getTrueTagKey(tagId), ifdId);
-    }
-
-    /**
-     * Returns the ExifTag in that tag's default IFD for a defined tag constant
-     * or null if none exists.
-     *
-     * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
-     * @return an {@link ExifTag} or null if none exists.
-     */
-    public ExifTag getTag(int tagId) {
-        int ifdId = getDefinedTagDefaultIfd(tagId);
-        return getTag(tagId, ifdId);
-    }
-
-    /**
-     * Gets a tag value for an IFD other than the tag's default.
-     *
-     * @see #getTagValue
-     */
-    public Object getTagValue(int tagId, int ifdId) {
-        ExifTag t = getTag(tagId, ifdId);
-        return (t == null) ? null : t.getValue();
-    }
-
-    /**
-     * Returns the value of the ExifTag in that tag's default IFD for a defined
-     * tag constant or null if none exists or the value could not be cast into
-     * the return type.
-     *
-     * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
-     * @return the value of the ExifTag or null if none exists.
-     */
-    public Object getTagValue(int tagId) {
-        int ifdId = getDefinedTagDefaultIfd(tagId);
-        return getTagValue(tagId, ifdId);
-    }
-
-    /*
-     * Getter methods that are similar to getTagValue. Null is returned if the
-     * tag value cannot be cast into the return type.
-     */
-
-    /**
-     * @see #getTagValue
-     */
-    public String getTagStringValue(int tagId, int ifdId) {
-        ExifTag t = getTag(tagId, ifdId);
-        if (t == null) {
-            return null;
-        }
-        return t.getValueAsString();
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public String getTagStringValue(int tagId) {
-        int ifdId = getDefinedTagDefaultIfd(tagId);
-        return getTagStringValue(tagId, ifdId);
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public Long getTagLongValue(int tagId, int ifdId) {
-        long[] l = getTagLongValues(tagId, ifdId);
-        if (l == null || l.length <= 0) {
-            return null;
-        }
-        return Long.valueOf(l[0]);
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public Long getTagLongValue(int tagId) {
-        int ifdId = getDefinedTagDefaultIfd(tagId);
-        return getTagLongValue(tagId, ifdId);
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public Integer getTagIntValue(int tagId, int ifdId) {
-        int[] l = getTagIntValues(tagId, ifdId);
-        if (l == null || l.length <= 0) {
-            return null;
-        }
-        return Integer.valueOf(l[0]);
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public Integer getTagIntValue(int tagId) {
-        int ifdId = getDefinedTagDefaultIfd(tagId);
-        return getTagIntValue(tagId, ifdId);
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public Byte getTagByteValue(int tagId, int ifdId) {
-        byte[] l = getTagByteValues(tagId, ifdId);
-        if (l == null || l.length <= 0) {
-            return null;
-        }
-        return Byte.valueOf(l[0]);
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public Byte getTagByteValue(int tagId) {
-        int ifdId = getDefinedTagDefaultIfd(tagId);
-        return getTagByteValue(tagId, ifdId);
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public Rational getTagRationalValue(int tagId, int ifdId) {
-        Rational[] l = getTagRationalValues(tagId, ifdId);
-        if (l == null || l.length == 0) {
-            return null;
-        }
-        return new Rational(l[0]);
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public Rational getTagRationalValue(int tagId) {
-        int ifdId = getDefinedTagDefaultIfd(tagId);
-        return getTagRationalValue(tagId, ifdId);
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public long[] getTagLongValues(int tagId, int ifdId) {
-        ExifTag t = getTag(tagId, ifdId);
-        if (t == null) {
-            return null;
-        }
-        return t.getValueAsLongs();
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public long[] getTagLongValues(int tagId) {
-        int ifdId = getDefinedTagDefaultIfd(tagId);
-        return getTagLongValues(tagId, ifdId);
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public int[] getTagIntValues(int tagId, int ifdId) {
-        ExifTag t = getTag(tagId, ifdId);
-        if (t == null) {
-            return null;
-        }
-        return t.getValueAsInts();
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public int[] getTagIntValues(int tagId) {
-        int ifdId = getDefinedTagDefaultIfd(tagId);
-        return getTagIntValues(tagId, ifdId);
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public byte[] getTagByteValues(int tagId, int ifdId) {
-        ExifTag t = getTag(tagId, ifdId);
-        if (t == null) {
-            return null;
-        }
-        return t.getValueAsBytes();
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public byte[] getTagByteValues(int tagId) {
-        int ifdId = getDefinedTagDefaultIfd(tagId);
-        return getTagByteValues(tagId, ifdId);
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public Rational[] getTagRationalValues(int tagId, int ifdId) {
-        ExifTag t = getTag(tagId, ifdId);
-        if (t == null) {
-            return null;
-        }
-        return t.getValueAsRationals();
-    }
-
-    /**
-     * @see #getTagValue
-     */
-    public Rational[] getTagRationalValues(int tagId) {
-        int ifdId = getDefinedTagDefaultIfd(tagId);
-        return getTagRationalValues(tagId, ifdId);
-    }
-
-    /**
-     * Checks whether a tag has a defined number of elements.
-     *
-     * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
-     * @return true if the tag has a defined number of elements.
-     */
-    public boolean isTagCountDefined(int tagId) {
-        int info = getTagInfo().get(tagId);
-        // No value in info can be zero, as all tags have a non-zero type
-        if (info == 0) {
-            return false;
-        }
-        return getComponentCountFromInfo(info) != ExifTag.SIZE_UNDEFINED;
-    }
-
-    /**
-     * Gets the defined number of elements for a tag.
-     *
-     * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
-     * @return the number of elements or {@link ExifTag#SIZE_UNDEFINED} if the
-     *         tag or the number of elements is not defined.
-     */
-    public int getDefinedTagCount(int tagId) {
-        int info = getTagInfo().get(tagId);
-        if (info == 0) {
-            return ExifTag.SIZE_UNDEFINED;
-        }
-        return getComponentCountFromInfo(info);
-    }
-
-    /**
-     * Gets the number of elements for an ExifTag in a given IFD.
-     *
-     * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
-     * @param ifdId the IFD containing the ExifTag to check.
-     * @return the number of elements in the ExifTag, if the tag's size is
-     *         undefined this will return the actual number of elements that is
-     *         in the ExifTag's value.
-     */
-    public int getActualTagCount(int tagId, int ifdId) {
-        ExifTag t = getTag(tagId, ifdId);
-        if (t == null) {
-            return 0;
-        }
-        return t.getComponentCount();
-    }
-
-    /**
-     * Gets the default IFD for a tag.
-     *
-     * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
-     * @return the default IFD for a tag definition or {@link #IFD_NULL} if no
-     *         definition exists.
-     */
-    public int getDefinedTagDefaultIfd(int tagId) {
-        int info = getTagInfo().get(tagId);
-        if (info == DEFINITION_NULL) {
-            return IFD_NULL;
-        }
-        return getTrueIfd(tagId);
-    }
-
-    /**
-     * Gets the defined type for a tag.
-     *
-     * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
-     * @return the type.
-     * @see ExifTag#getDataType()
-     */
-    public short getDefinedTagType(int tagId) {
-        int info = getTagInfo().get(tagId);
-        if (info == 0) {
-            return -1;
-        }
-        return getTypeFromInfo(info);
-    }
-
-    /**
-     * Returns true if tag TID is one of the following: {@link TAG_EXIF_IFD},
-     * {@link TAG_GPS_IFD}, {@link TAG_JPEG_INTERCHANGE_FORMAT},
-     * {@link TAG_STRIP_OFFSETS}, {@link TAG_INTEROPERABILITY_IFD}
-     * <p>
-     * Note: defining tags with these TID's is disallowed.
-     *
-     * @param tag a tag's TID (can be obtained from a defined tag constant with
-     *            {@link #getTrueTagKey}).
-     * @return true if the TID is that of an offset tag.
-     */
-    protected static boolean isOffsetTag(short tag) {
-        return sOffsetTags.contains(tag);
-    }
-
-    /**
-     * Creates a tag for a defined tag constant in a given IFD if that IFD is
-     * allowed for the tag.  This method will fail anytime the appropriate
-     * {@link ExifTag#setValue} for this tag's datatype would fail.
-     *
-     * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
-     * @param ifdId the IFD that the tag should be in.
-     * @param val the value of the tag to set.
-     * @return an ExifTag object or null if one could not be constructed.
-     * @see #buildTag
-     */
-    public ExifTag buildTag(int tagId, int ifdId, Object val) {
-        int info = getTagInfo().get(tagId);
-        if (info == 0 || val == null) {
-            return null;
-        }
-        short type = getTypeFromInfo(info);
-        int definedCount = getComponentCountFromInfo(info);
-        boolean hasDefinedCount = (definedCount != ExifTag.SIZE_UNDEFINED);
-        if (!ExifInterface.isIfdAllowed(info, ifdId)) {
-            return null;
-        }
-        ExifTag t = new ExifTag(getTrueTagKey(tagId), type, definedCount, ifdId, hasDefinedCount);
-        if (!t.setValue(val)) {
-            return null;
-        }
-        return t;
-    }
-
-    /**
-     * Creates a tag for a defined tag constant in the tag's default IFD.
-     *
-     * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
-     * @param val the tag's value.
-     * @return an ExifTag object.
-     */
-    public ExifTag buildTag(int tagId, Object val) {
-        int ifdId = getTrueIfd(tagId);
-        return buildTag(tagId, ifdId, val);
-    }
-
-    protected ExifTag buildUninitializedTag(int tagId) {
-        int info = getTagInfo().get(tagId);
-        if (info == 0) {
-            return null;
-        }
-        short type = getTypeFromInfo(info);
-        int definedCount = getComponentCountFromInfo(info);
-        boolean hasDefinedCount = (definedCount != ExifTag.SIZE_UNDEFINED);
-        int ifdId = getTrueIfd(tagId);
-        ExifTag t = new ExifTag(getTrueTagKey(tagId), type, definedCount, ifdId, hasDefinedCount);
-        return t;
-    }
-
-    /**
-     * Sets the value of an ExifTag if it exists in the given IFD. The value
-     * must be the correct type and length for that ExifTag.
-     *
-     * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
-     * @param ifdId the IFD that the ExifTag is in.
-     * @param val the value to set.
-     * @return true if success, false if the ExifTag doesn't exist or the value
-     *         is the wrong type/length.
-     * @see #setTagValue
-     */
-    public boolean setTagValue(int tagId, int ifdId, Object val) {
-        ExifTag t = getTag(tagId, ifdId);
-        if (t == null) {
-            return false;
-        }
-        return t.setValue(val);
-    }
-
-    /**
-     * Sets the value of an ExifTag if it exists it's default IFD. The value
-     * must be the correct type and length for that ExifTag.
-     *
-     * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
-     * @param val the value to set.
-     * @return true if success, false if the ExifTag doesn't exist or the value
-     *         is the wrong type/length.
-     */
-    public boolean setTagValue(int tagId, Object val) {
-        int ifdId = getDefinedTagDefaultIfd(tagId);
-        return setTagValue(tagId, ifdId, val);
-    }
-
-    /**
-     * Puts an ExifTag into this ExifInterface object's tags, removing a
-     * previous ExifTag with the same TID and IFD. The IFD it is put into will
-     * be the one the tag was created with in {@link #buildTag}.
-     *
-     * @param tag an ExifTag to put into this ExifInterface's tags.
-     * @return the previous ExifTag with the same TID and IFD or null if none
-     *         exists.
-     */
-    public ExifTag setTag(ExifTag tag) {
-        return mData.addTag(tag);
-    }
-
-    /**
-     * Puts a collection of ExifTags into this ExifInterface objects's tags. Any
-     * previous ExifTags with the same TID and IFDs will be removed.
-     *
-     * @param tags a Collection of ExifTags.
-     * @see #setTag
-     */
-    public void setTags(Collection<ExifTag> tags) {
-        for (ExifTag t : tags) {
-            setTag(t);
-        }
-    }
-
-    /**
-     * Removes the ExifTag for a tag constant from the given IFD.
-     *
-     * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
-     * @param ifdId the IFD of the ExifTag to remove.
-     */
-    public void deleteTag(int tagId, int ifdId) {
-        mData.removeTag(getTrueTagKey(tagId), ifdId);
-    }
-
-    /**
-     * Removes the ExifTag for a tag constant from that tag's default IFD.
-     *
-     * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
-     */
-    public void deleteTag(int tagId) {
-        int ifdId = getDefinedTagDefaultIfd(tagId);
-        deleteTag(tagId, ifdId);
-    }
-
-    /**
-     * Creates a new tag definition in this ExifInterface object for a given TID
-     * and default IFD. Creating a definition with the same TID and default IFD
-     * as a previous definition will override it.
-     *
-     * @param tagId the TID for the tag.
-     * @param defaultIfd the default IFD for the tag.
-     * @param tagType the type of the tag (see {@link ExifTag#getDataType()}).
-     * @param defaultComponentCount the number of elements of this tag's type in
-     *            the tags value.
-     * @param allowedIfds the IFD's this tag is allowed to be put in.
-     * @return the defined tag constant (e.g. {@link #TAG_IMAGE_WIDTH}) or
-     *         {@link #TAG_NULL} if the definition could not be made.
-     */
-    public int setTagDefinition(short tagId, int defaultIfd, short tagType,
-            short defaultComponentCount, int[] allowedIfds) {
-        if (sBannedDefines.contains(tagId)) {
-            return TAG_NULL;
-        }
-        if (ExifTag.isValidType(tagType) && ExifTag.isValidIfd(defaultIfd)) {
-            int tagDef = defineTag(defaultIfd, tagId);
-            if (tagDef == TAG_NULL) {
-                return TAG_NULL;
-            }
-            int[] otherDefs = getTagDefinitionsForTagId(tagId);
-            SparseIntArray infos = getTagInfo();
-            // Make sure defaultIfd is in allowedIfds
-            boolean defaultCheck = false;
-            for (int i : allowedIfds) {
-                if (defaultIfd == i) {
-                    defaultCheck = true;
-                }
-                if (!ExifTag.isValidIfd(i)) {
-                    return TAG_NULL;
-                }
-            }
-            if (!defaultCheck) {
-                return TAG_NULL;
-            }
-
-            int ifdFlags = getFlagsFromAllowedIfds(allowedIfds);
-            // Make sure no identical tags can exist in allowedIfds
-            if (otherDefs != null) {
-                for (int def : otherDefs) {
-                    int tagInfo = infos.get(def);
-                    int allowedFlags = getAllowedIfdFlagsFromInfo(tagInfo);
-                    if ((ifdFlags & allowedFlags) != 0) {
-                        return TAG_NULL;
-                    }
-                }
-            }
-            getTagInfo().put(tagDef, ifdFlags << 24 | (tagType << 16) | defaultComponentCount);
-            return tagDef;
-        }
-        return TAG_NULL;
-    }
-
-    protected int getTagDefinition(short tagId, int defaultIfd) {
-        return getTagInfo().get(defineTag(defaultIfd, tagId));
-    }
-
-    protected int[] getTagDefinitionsForTagId(short tagId) {
-        int[] ifds = IfdData.getIfds();
-        int[] defs = new int[ifds.length];
-        int counter = 0;
-        SparseIntArray infos = getTagInfo();
-        for (int i : ifds) {
-            int def = defineTag(i, tagId);
-            if (infos.get(def) != DEFINITION_NULL) {
-                defs[counter++] = def;
-            }
-        }
-        if (counter == 0) {
-            return null;
-        }
-
-        return Arrays.copyOfRange(defs, 0, counter);
-    }
-
-    protected int getTagDefinitionForTag(ExifTag tag) {
-        short type = tag.getDataType();
-        int count = tag.getComponentCount();
-        int ifd = tag.getIfd();
-        return getTagDefinitionForTag(tag.getTagId(), type, count, ifd);
-    }
-
-    protected int getTagDefinitionForTag(short tagId, short type, int count, int ifd) {
-        int[] defs = getTagDefinitionsForTagId(tagId);
-        if (defs == null) {
-            return TAG_NULL;
-        }
-        SparseIntArray infos = getTagInfo();
-        int ret = TAG_NULL;
-        for (int i : defs) {
-            int info = infos.get(i);
-            short def_type = getTypeFromInfo(info);
-            int def_count = getComponentCountFromInfo(info);
-            int[] def_ifds = getAllowedIfdsFromInfo(info);
-            boolean valid_ifd = false;
-            for (int j : def_ifds) {
-                if (j == ifd) {
-                    valid_ifd = true;
-                    break;
-                }
-            }
-            if (valid_ifd && type == def_type
-                    && (count == def_count || def_count == ExifTag.SIZE_UNDEFINED)) {
-                ret = i;
-                break;
-            }
-        }
-        return ret;
-    }
-
-    /**
-     * Removes a tag definition for given defined tag constant.
-     *
-     * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}.
-     */
-    public void removeTagDefinition(int tagId) {
-        getTagInfo().delete(tagId);
-    }
-
-    /**
-     * Resets tag definitions to the default ones.
-     */
-    public void resetTagDefinitions() {
-        mTagInfo = null;
-    }
-
-    /**
-     * Returns the thumbnail from IFD1 as a bitmap, or null if none exists.
-     *
-     * @return the thumbnail as a bitmap.
-     */
-    public Bitmap getThumbnailBitmap() {
-        if (mData.hasCompressedThumbnail()) {
-            byte[] thumb = mData.getCompressedThumbnail();
-            return BitmapFactory.decodeByteArray(thumb, 0, thumb.length);
-        } else if (mData.hasUncompressedStrip()) {
-            // TODO: implement uncompressed
-        }
-        return null;
-    }
-
-    /**
-     * Returns the thumbnail from IFD1 as a byte array, or null if none exists.
-     * The bytes may either be an uncompressed strip as specified in the exif
-     * standard or a jpeg compressed image.
-     *
-     * @return the thumbnail as a byte array.
-     */
-    public byte[] getThumbnailBytes() {
-        if (mData.hasCompressedThumbnail()) {
-            return mData.getCompressedThumbnail();
-        } else if (mData.hasUncompressedStrip()) {
-            // TODO: implement this
-        }
-        return null;
-    }
-
-    /**
-     * Returns the thumbnail if it is jpeg compressed, or null if none exists.
-     *
-     * @return the thumbnail as a byte array.
-     */
-    public byte[] getThumbnail() {
-        return mData.getCompressedThumbnail();
-    }
-
-    /**
-     * Check if thumbnail is compressed.
-     *
-     * @return true if the thumbnail is compressed.
-     */
-    public boolean isThumbnailCompressed() {
-        return mData.hasCompressedThumbnail();
-    }
-
-    /**
-     * Check if thumbnail exists.
-     *
-     * @return true if a compressed thumbnail exists.
-     */
-    public boolean hasThumbnail() {
-        // TODO: add back in uncompressed strip
-        return mData.hasCompressedThumbnail();
-    }
-
-    // TODO: uncompressed thumbnail setters
-
-    /**
-     * Sets the thumbnail to be a jpeg compressed image. Clears any prior
-     * thumbnail.
-     *
-     * @param thumb a byte array containing a jpeg compressed image.
-     * @return true if the thumbnail was set.
-     */
-    public boolean setCompressedThumbnail(byte[] thumb) {
-        mData.clearThumbnailAndStrips();
-        mData.setCompressedThumbnail(thumb);
-        return true;
-    }
-
-    /**
-     * Sets the thumbnail to be a jpeg compressed bitmap. Clears any prior
-     * thumbnail.
-     *
-     * @param thumb a bitmap to compress to a jpeg thumbnail.
-     * @return true if the thumbnail was set.
-     */
-    public boolean setCompressedThumbnail(Bitmap thumb) {
-        ByteArrayOutputStream thumbnail = new ByteArrayOutputStream();
-        if (!thumb.compress(Bitmap.CompressFormat.JPEG, 90, thumbnail)) {
-            return false;
-        }
-        return setCompressedThumbnail(thumbnail.toByteArray());
-    }
-
-    /**
-     * Clears the compressed thumbnail if it exists.
-     */
-    public void removeCompressedThumbnail() {
-        mData.setCompressedThumbnail(null);
-    }
-
-    // Convenience methods:
-
-    /**
-     * Decodes the user comment tag into string as specified in the EXIF
-     * standard. Returns null if decoding failed.
-     */
-    public String getUserComment() {
-        return mData.getUserComment();
-    }
-
-    /**
-     * Returns the Orientation ExifTag value for a given number of degrees.
-     *
-     * @param degrees the amount an image is rotated in degrees.
-     */
-    public static short getOrientationValueForRotation(int degrees) {
-        degrees %= 360;
-        if (degrees < 0) {
-            degrees += 360;
-        }
-        if (degrees < 90) {
-            return Orientation.TOP_LEFT; // 0 degrees
-        } else if (degrees < 180) {
-            return Orientation.RIGHT_TOP; // 90 degrees cw
-        } else if (degrees < 270) {
-            return Orientation.BOTTOM_LEFT; // 180 degrees
-        } else {
-            return Orientation.RIGHT_BOTTOM; // 270 degrees cw
-        }
-    }
-
-    /**
-     * Returns the rotation degrees corresponding to an ExifTag Orientation
-     * value.
-     *
-     * @param orientation the ExifTag Orientation value.
-     */
-    public static int getRotationForOrientationValue(short orientation) {
-        switch (orientation) {
-            case Orientation.TOP_LEFT:
-                return 0;
-            case Orientation.RIGHT_TOP:
-                return 90;
-            case Orientation.BOTTOM_LEFT:
-                return 180;
-            case Orientation.RIGHT_BOTTOM:
-                return 270;
-            default:
-                return 0;
-        }
-    }
-
-    /**
-     * Gets the double representation of the GPS latitude or longitude
-     * coordinate.
-     *
-     * @param coordinate an array of 3 Rationals representing the degrees,
-     *            minutes, and seconds of the GPS location as defined in the
-     *            exif specification.
-     * @param reference a GPS reference reperesented by a String containing "N",
-     *            "S", "E", or "W".
-     * @return the GPS coordinate represented as degrees + minutes/60 +
-     *         seconds/3600
-     */
-    public static double convertLatOrLongToDouble(Rational[] coordinate, String reference) {
-        try {
-            double degrees = coordinate[0].toDouble();
-            double minutes = coordinate[1].toDouble();
-            double seconds = coordinate[2].toDouble();
-            double result = degrees + minutes / 60.0 + seconds / 3600.0;
-            if ((reference.equals("S") || reference.equals("W"))) {
-                return -result;
-            }
-            return result;
-        } catch (ArrayIndexOutOfBoundsException e) {
-            throw new IllegalArgumentException();
-        }
-    }
-
-    /**
-     * Gets the GPS latitude and longitude as a pair of doubles from this
-     * ExifInterface object's tags, or null if the necessary tags do not exist.
-     *
-     * @return an array of 2 doubles containing the latitude, and longitude
-     *         respectively.
-     * @see #convertLatOrLongToDouble
-     */
-    public double[] getLatLongAsDoubles() {
-        Rational[] latitude = getTagRationalValues(TAG_GPS_LATITUDE);
-        String latitudeRef = getTagStringValue(TAG_GPS_LATITUDE_REF);
-        Rational[] longitude = getTagRationalValues(TAG_GPS_LONGITUDE);
-        String longitudeRef = getTagStringValue(TAG_GPS_LONGITUDE_REF);
-        if (latitude == null || longitude == null || latitudeRef == null || longitudeRef == null
-                || latitude.length < 3 || longitude.length < 3) {
-            return null;
-        }
-        double[] latLon = new double[2];
-        latLon[0] = convertLatOrLongToDouble(latitude, latitudeRef);
-        latLon[1] = convertLatOrLongToDouble(longitude, longitudeRef);
-        return latLon;
-    }
-
-    private static final String GPS_DATE_FORMAT_STR = "yyyy:MM:dd";
-    private static final String DATETIME_FORMAT_STR = "yyyy:MM:dd kk:mm:ss";
-    private final DateFormat mDateTimeStampFormat = new SimpleDateFormat(DATETIME_FORMAT_STR);
-    private final DateFormat mGPSDateStampFormat = new SimpleDateFormat(GPS_DATE_FORMAT_STR);
-    private final Calendar mGPSTimeStampCalendar = Calendar
-            .getInstance(TimeZone.getTimeZone("UTC"));
-
-    /**
-     * Creates, formats, and sets the DateTimeStamp tag for one of:
-     * {@link #TAG_DATE_TIME}, {@link #TAG_DATE_TIME_DIGITIZED},
-     * {@link #TAG_DATE_TIME_ORIGINAL}.
-     *
-     * @param tagId one of the DateTimeStamp tags.
-     * @param timestamp a timestamp to format.
-     * @param timezone a TimeZone object.
-     * @return true if success, false if the tag could not be set.
-     */
-    public boolean addDateTimeStampTag(int tagId, long timestamp, TimeZone timezone) {
-        if (tagId == TAG_DATE_TIME || tagId == TAG_DATE_TIME_DIGITIZED
-                || tagId == TAG_DATE_TIME_ORIGINAL) {
-            mDateTimeStampFormat.setTimeZone(timezone);
-            ExifTag t = buildTag(tagId, mDateTimeStampFormat.format(timestamp));
-            if (t == null) {
-                return false;
-            }
-            setTag(t);
-        } else {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Creates and sets all to the GPS tags for a give latitude and longitude.
-     *
-     * @param latitude a GPS latitude coordinate.
-     * @param longitude a GPS longitude coordinate.
-     * @return true if success, false if they could not be created or set.
-     */
-    public boolean addGpsTags(double latitude, double longitude) {
-        ExifTag latTag = buildTag(TAG_GPS_LATITUDE, toExifLatLong(latitude));
-        ExifTag longTag = buildTag(TAG_GPS_LONGITUDE, toExifLatLong(longitude));
-        ExifTag latRefTag = buildTag(TAG_GPS_LATITUDE_REF,
-                latitude >= 0 ? ExifInterface.GpsLatitudeRef.NORTH
-                        : ExifInterface.GpsLatitudeRef.SOUTH);
-        ExifTag longRefTag = buildTag(TAG_GPS_LONGITUDE_REF,
-                longitude >= 0 ? ExifInterface.GpsLongitudeRef.EAST
-                        : ExifInterface.GpsLongitudeRef.WEST);
-        if (latTag == null || longTag == null || latRefTag == null || longRefTag == null) {
-            return false;
-        }
-        setTag(latTag);
-        setTag(longTag);
-        setTag(latRefTag);
-        setTag(longRefTag);
-        return true;
-    }
-
-    /**
-     * Creates and sets the GPS timestamp tag.
-     *
-     * @param timestamp a GPS timestamp.
-     * @return true if success, false if could not be created or set.
-     */
-    public boolean addGpsDateTimeStampTag(long timestamp) {
-        ExifTag t = buildTag(TAG_GPS_DATE_STAMP, mGPSDateStampFormat.format(timestamp));
-        if (t == null) {
-            return false;
-        }
-        setTag(t);
-        mGPSTimeStampCalendar.setTimeInMillis(timestamp);
-        t = buildTag(TAG_GPS_TIME_STAMP, new Rational[] {
-                new Rational(mGPSTimeStampCalendar.get(Calendar.HOUR_OF_DAY), 1),
-                new Rational(mGPSTimeStampCalendar.get(Calendar.MINUTE), 1),
-                new Rational(mGPSTimeStampCalendar.get(Calendar.SECOND), 1)
-        });
-        if (t == null) {
-            return false;
-        }
-        setTag(t);
-        return true;
-    }
-
-    private static Rational[] toExifLatLong(double value) {
-        // convert to the format dd/1 mm/1 ssss/100
-        value = Math.abs(value);
-        int degrees = (int) value;
-        value = (value - degrees) * 60;
-        int minutes = (int) value;
-        value = (value - minutes) * 6000;
-        int seconds = (int) value;
-        return new Rational[] {
-                new Rational(degrees, 1), new Rational(minutes, 1), new Rational(seconds, 100)
-        };
-    }
-
-    private void doExifStreamIO(InputStream is, OutputStream os) throws IOException {
-        byte[] buf = new byte[1024];
-        int ret = is.read(buf, 0, 1024);
-        while (ret != -1) {
-            os.write(buf, 0, ret);
-            ret = is.read(buf, 0, 1024);
-        }
-    }
-
-    protected static void closeSilently(Closeable c) {
-        if (c != null) {
-            try {
-                c.close();
-            } catch (Throwable e) {
-                // ignored
-            }
-        }
-    }
-
-    private SparseIntArray mTagInfo = null;
-
-    protected SparseIntArray getTagInfo() {
-        if (mTagInfo == null) {
-            mTagInfo = new SparseIntArray();
-            initTagInfo();
-        }
-        return mTagInfo;
-    }
-
-    private void initTagInfo() {
-        /**
-         * We put tag information in a 4-bytes integer. The first byte a bitmask
-         * representing the allowed IFDs of the tag, the second byte is the data
-         * type, and the last two byte are a short value indicating the default
-         * component count of this tag.
-         */
-        // IFD0 tags
-        int[] ifdAllowedIfds = {
-                IfdId.TYPE_IFD_0, IfdId.TYPE_IFD_1
-        };
-        int ifdFlags = getFlagsFromAllowedIfds(ifdAllowedIfds) << 24;
-        mTagInfo.put(ExifInterface.TAG_MAKE,
-                ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_IMAGE_WIDTH,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_IMAGE_LENGTH,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_BITS_PER_SAMPLE,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 3);
-        mTagInfo.put(ExifInterface.TAG_COMPRESSION,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_PHOTOMETRIC_INTERPRETATION,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_ORIENTATION, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16
-                | 1);
-        mTagInfo.put(ExifInterface.TAG_SAMPLES_PER_PIXEL,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_PLANAR_CONFIGURATION,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_Y_CB_CR_SUB_SAMPLING,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 2);
-        mTagInfo.put(ExifInterface.TAG_Y_CB_CR_POSITIONING,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_X_RESOLUTION,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_Y_RESOLUTION,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_RESOLUTION_UNIT,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_STRIP_OFFSETS,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_ROWS_PER_STRIP,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_STRIP_BYTE_COUNTS,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_TRANSFER_FUNCTION,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 3 * 256);
-        mTagInfo.put(ExifInterface.TAG_WHITE_POINT,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 2);
-        mTagInfo.put(ExifInterface.TAG_PRIMARY_CHROMATICITIES,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 6);
-        mTagInfo.put(ExifInterface.TAG_Y_CB_CR_COEFFICIENTS,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 3);
-        mTagInfo.put(ExifInterface.TAG_REFERENCE_BLACK_WHITE,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 6);
-        mTagInfo.put(ExifInterface.TAG_DATE_TIME,
-                ifdFlags | ExifTag.TYPE_ASCII << 16 | 20);
-        mTagInfo.put(ExifInterface.TAG_IMAGE_DESCRIPTION,
-                ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_MAKE,
-                ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_MODEL,
-                ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_SOFTWARE,
-                ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_ARTIST,
-                ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_COPYRIGHT,
-                ifdFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_EXIF_IFD,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_GPS_IFD,
-                ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
-        // IFD1 tags
-        int[] ifd1AllowedIfds = {
-            IfdId.TYPE_IFD_1
-        };
-        int ifdFlags1 = getFlagsFromAllowedIfds(ifd1AllowedIfds) << 24;
-        mTagInfo.put(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT,
-                ifdFlags1 | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH,
-                ifdFlags1 | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
-        // Exif tags
-        int[] exifAllowedIfds = {
-            IfdId.TYPE_IFD_EXIF
-        };
-        int exifFlags = getFlagsFromAllowedIfds(exifAllowedIfds) << 24;
-        mTagInfo.put(ExifInterface.TAG_EXIF_VERSION,
-                exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4);
-        mTagInfo.put(ExifInterface.TAG_FLASHPIX_VERSION,
-                exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4);
-        mTagInfo.put(ExifInterface.TAG_COLOR_SPACE,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_COMPONENTS_CONFIGURATION,
-                exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4);
-        mTagInfo.put(ExifInterface.TAG_COMPRESSED_BITS_PER_PIXEL,
-                exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_PIXEL_X_DIMENSION,
-                exifFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_PIXEL_Y_DIMENSION,
-                exifFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_MAKER_NOTE,
-                exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_USER_COMMENT,
-                exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_RELATED_SOUND_FILE,
-                exifFlags | ExifTag.TYPE_ASCII << 16 | 13);
-        mTagInfo.put(ExifInterface.TAG_DATE_TIME_ORIGINAL,
-                exifFlags | ExifTag.TYPE_ASCII << 16 | 20);
-        mTagInfo.put(ExifInterface.TAG_DATE_TIME_DIGITIZED,
-                exifFlags | ExifTag.TYPE_ASCII << 16 | 20);
-        mTagInfo.put(ExifInterface.TAG_SUB_SEC_TIME,
-                exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_SUB_SEC_TIME_ORIGINAL,
-                exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_SUB_SEC_TIME_DIGITIZED,
-                exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_IMAGE_UNIQUE_ID,
-                exifFlags | ExifTag.TYPE_ASCII << 16 | 33);
-        mTagInfo.put(ExifInterface.TAG_EXPOSURE_TIME,
-                exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_F_NUMBER,
-                exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_EXPOSURE_PROGRAM,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_SPECTRAL_SENSITIVITY,
-                exifFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_ISO_SPEED_RATINGS,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_OECF,
-                exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_SHUTTER_SPEED_VALUE,
-                exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_APERTURE_VALUE,
-                exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_BRIGHTNESS_VALUE,
-                exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_EXPOSURE_BIAS_VALUE,
-                exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_MAX_APERTURE_VALUE,
-                exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_SUBJECT_DISTANCE,
-                exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_METERING_MODE,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_LIGHT_SOURCE,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_FLASH,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_FOCAL_LENGTH,
-                exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_SUBJECT_AREA,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_FLASH_ENERGY,
-                exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_SPATIAL_FREQUENCY_RESPONSE,
-                exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_FOCAL_PLANE_X_RESOLUTION,
-                exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_FOCAL_PLANE_Y_RESOLUTION,
-                exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_FOCAL_PLANE_RESOLUTION_UNIT,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_SUBJECT_LOCATION,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 2);
-        mTagInfo.put(ExifInterface.TAG_EXPOSURE_INDEX,
-                exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_SENSING_METHOD,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_FILE_SOURCE,
-                exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_SCENE_TYPE,
-                exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_CFA_PATTERN,
-                exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_CUSTOM_RENDERED,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_EXPOSURE_MODE,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_WHITE_BALANCE,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_DIGITAL_ZOOM_RATIO,
-                exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_FOCAL_LENGTH_IN_35_MM_FILE,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_SCENE_CAPTURE_TYPE,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_GAIN_CONTROL,
-                exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_CONTRAST,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_SATURATION,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_SHARPNESS,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_DEVICE_SETTING_DESCRIPTION,
-                exifFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_SUBJECT_DISTANCE_RANGE,
-                exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_INTEROPERABILITY_IFD, exifFlags
-                | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1);
-        // GPS tag
-        int[] gpsAllowedIfds = {
-            IfdId.TYPE_IFD_GPS
-        };
-        int gpsFlags = getFlagsFromAllowedIfds(gpsAllowedIfds) << 24;
-        mTagInfo.put(ExifInterface.TAG_GPS_VERSION_ID,
-                gpsFlags | ExifTag.TYPE_UNSIGNED_BYTE << 16 | 4);
-        mTagInfo.put(ExifInterface.TAG_GPS_LATITUDE_REF,
-                gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
-        mTagInfo.put(ExifInterface.TAG_GPS_LONGITUDE_REF,
-                gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
-        mTagInfo.put(ExifInterface.TAG_GPS_LATITUDE,
-                gpsFlags | ExifTag.TYPE_RATIONAL << 16 | 3);
-        mTagInfo.put(ExifInterface.TAG_GPS_LONGITUDE,
-                gpsFlags | ExifTag.TYPE_RATIONAL << 16 | 3);
-        mTagInfo.put(ExifInterface.TAG_GPS_ALTITUDE_REF,
-                gpsFlags | ExifTag.TYPE_UNSIGNED_BYTE << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_GPS_ALTITUDE,
-                gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_GPS_TIME_STAMP,
-                gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 3);
-        mTagInfo.put(ExifInterface.TAG_GPS_SATTELLITES,
-                gpsFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_GPS_STATUS,
-                gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
-        mTagInfo.put(ExifInterface.TAG_GPS_MEASURE_MODE,
-                gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
-        mTagInfo.put(ExifInterface.TAG_GPS_DOP,
-                gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_GPS_SPEED_REF,
-                gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
-        mTagInfo.put(ExifInterface.TAG_GPS_SPEED,
-                gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_GPS_TRACK_REF,
-                gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
-        mTagInfo.put(ExifInterface.TAG_GPS_TRACK,
-                gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_GPS_IMG_DIRECTION_REF,
-                gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
-        mTagInfo.put(ExifInterface.TAG_GPS_IMG_DIRECTION,
-                gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_GPS_MAP_DATUM,
-                gpsFlags | ExifTag.TYPE_ASCII << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_GPS_DEST_LATITUDE_REF,
-                gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
-        mTagInfo.put(ExifInterface.TAG_GPS_DEST_LATITUDE,
-                gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_GPS_DEST_BEARING_REF,
-                gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
-        mTagInfo.put(ExifInterface.TAG_GPS_DEST_BEARING,
-                gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_GPS_DEST_DISTANCE_REF,
-                gpsFlags | ExifTag.TYPE_ASCII << 16 | 2);
-        mTagInfo.put(ExifInterface.TAG_GPS_DEST_DISTANCE,
-                gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1);
-        mTagInfo.put(ExifInterface.TAG_GPS_PROCESSING_METHOD,
-                gpsFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_GPS_AREA_INFORMATION,
-                gpsFlags | ExifTag.TYPE_UNDEFINED << 16 | ExifTag.SIZE_UNDEFINED);
-        mTagInfo.put(ExifInterface.TAG_GPS_DATE_STAMP,
-                gpsFlags | ExifTag.TYPE_ASCII << 16 | 11);
-        mTagInfo.put(ExifInterface.TAG_GPS_DIFFERENTIAL,
-                gpsFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 11);
-        // Interoperability tag
-        int[] interopAllowedIfds = {
-            IfdId.TYPE_IFD_INTEROPERABILITY
-        };
-        int interopFlags = getFlagsFromAllowedIfds(interopAllowedIfds) << 24;
-        mTagInfo.put(TAG_INTEROPERABILITY_INDEX, interopFlags | ExifTag.TYPE_ASCII << 16
-                | ExifTag.SIZE_UNDEFINED);
-    }
-
-    protected static int getAllowedIfdFlagsFromInfo(int info) {
-        return info >>> 24;
-    }
-
-    protected static int[] getAllowedIfdsFromInfo(int info) {
-        int ifdFlags = getAllowedIfdFlagsFromInfo(info);
-        int[] ifds = IfdData.getIfds();
-        ArrayList<Integer> l = new ArrayList<Integer>();
-        for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
-            int flag = (ifdFlags >> i) & 1;
-            if (flag == 1) {
-                l.add(ifds[i]);
-            }
-        }
-        if (l.size() <= 0) {
-            return null;
-        }
-        int[] ret = new int[l.size()];
-        int j = 0;
-        for (int i : l) {
-            ret[j++] = i;
-        }
-        return ret;
-    }
-
-    protected static boolean isIfdAllowed(int info, int ifd) {
-        int[] ifds = IfdData.getIfds();
-        int ifdFlags = getAllowedIfdFlagsFromInfo(info);
-        for (int i = 0; i < ifds.length; i++) {
-            if (ifd == ifds[i] && ((ifdFlags >> i) & 1) == 1) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    protected static int getFlagsFromAllowedIfds(int[] allowedIfds) {
-        if (allowedIfds == null || allowedIfds.length == 0) {
-            return 0;
-        }
-        int flags = 0;
-        int[] ifds = IfdData.getIfds();
-        for (int i = 0; i < IfdId.TYPE_IFD_COUNT; i++) {
-            for (int j : allowedIfds) {
-                if (ifds[i] == j) {
-                    flags |= 1 << i;
-                    break;
-                }
-            }
-        }
-        return flags;
-    }
-
-    protected static short getTypeFromInfo(int info) {
-        return (short) ((info >> 16) & 0x0ff);
-    }
-
-    protected static int getComponentCountFromInfo(int info) {
-        return info & 0x0ffff;
-    }
-
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifInvalidFormatException.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifInvalidFormatException.java
deleted file mode 100644
index bf923ec..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifInvalidFormatException.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-public class ExifInvalidFormatException extends Exception {
-    public ExifInvalidFormatException(String meg) {
-        super(meg);
-    }
-}
\ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifModifier.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifModifier.java
deleted file mode 100644
index 0531cba..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifModifier.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-import java.util.List;
-
-class ExifModifier {
-    public static final String TAG = "ExifModifier";
-    public static final boolean DEBUG = false;
-    private final ByteBuffer mByteBuffer;
-    private final ExifData mTagToModified;
-    private final List<TagOffset> mTagOffsets = new ArrayList<TagOffset>();
-    private final ExifInterface mInterface;
-    private int mOffsetBase;
-
-    private static class TagOffset {
-        final int mOffset;
-        final ExifTag mTag;
-
-        TagOffset(ExifTag tag, int offset) {
-            mTag = tag;
-            mOffset = offset;
-        }
-    }
-
-    protected ExifModifier(ByteBuffer byteBuffer, ExifInterface iRef) throws IOException,
-            ExifInvalidFormatException {
-        mByteBuffer = byteBuffer;
-        mOffsetBase = byteBuffer.position();
-        mInterface = iRef;
-        InputStream is = null;
-        try {
-            is = new ByteBufferInputStream(byteBuffer);
-            // Do not require any IFD;
-            ExifParser parser = ExifParser.parse(is, mInterface);
-            mTagToModified = new ExifData(parser.getByteOrder());
-            mOffsetBase += parser.getTiffStartPosition();
-            mByteBuffer.position(0);
-        } finally {
-            ExifInterface.closeSilently(is);
-        }
-    }
-
-    protected ByteOrder getByteOrder() {
-        return mTagToModified.getByteOrder();
-    }
-
-    protected boolean commit() throws IOException, ExifInvalidFormatException {
-        InputStream is = null;
-        try {
-            is = new ByteBufferInputStream(mByteBuffer);
-            int flag = 0;
-            IfdData[] ifdDatas = new IfdData[] {
-                    mTagToModified.getIfdData(IfdId.TYPE_IFD_0),
-                    mTagToModified.getIfdData(IfdId.TYPE_IFD_1),
-                    mTagToModified.getIfdData(IfdId.TYPE_IFD_EXIF),
-                    mTagToModified.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY),
-                    mTagToModified.getIfdData(IfdId.TYPE_IFD_GPS)
-            };
-
-            if (ifdDatas[IfdId.TYPE_IFD_0] != null) {
-                flag |= ExifParser.OPTION_IFD_0;
-            }
-            if (ifdDatas[IfdId.TYPE_IFD_1] != null) {
-                flag |= ExifParser.OPTION_IFD_1;
-            }
-            if (ifdDatas[IfdId.TYPE_IFD_EXIF] != null) {
-                flag |= ExifParser.OPTION_IFD_EXIF;
-            }
-            if (ifdDatas[IfdId.TYPE_IFD_GPS] != null) {
-                flag |= ExifParser.OPTION_IFD_GPS;
-            }
-            if (ifdDatas[IfdId.TYPE_IFD_INTEROPERABILITY] != null) {
-                flag |= ExifParser.OPTION_IFD_INTEROPERABILITY;
-            }
-
-            ExifParser parser = ExifParser.parse(is, flag, mInterface);
-            int event = parser.next();
-            IfdData currIfd = null;
-            while (event != ExifParser.EVENT_END) {
-                switch (event) {
-                    case ExifParser.EVENT_START_OF_IFD:
-                        currIfd = ifdDatas[parser.getCurrentIfd()];
-                        if (currIfd == null) {
-                            parser.skipRemainingTagsInCurrentIfd();
-                        }
-                        break;
-                    case ExifParser.EVENT_NEW_TAG:
-                        ExifTag oldTag = parser.getTag();
-                        ExifTag newTag = currIfd.getTag(oldTag.getTagId());
-                        if (newTag != null) {
-                            if (newTag.getComponentCount() != oldTag.getComponentCount()
-                                    || newTag.getDataType() != oldTag.getDataType()) {
-                                return false;
-                            } else {
-                                mTagOffsets.add(new TagOffset(newTag, oldTag.getOffset()));
-                                currIfd.removeTag(oldTag.getTagId());
-                                if (currIfd.getTagCount() == 0) {
-                                    parser.skipRemainingTagsInCurrentIfd();
-                                }
-                            }
-                        }
-                        break;
-                }
-                event = parser.next();
-            }
-            for (IfdData ifd : ifdDatas) {
-                if (ifd != null && ifd.getTagCount() > 0) {
-                    return false;
-                }
-            }
-            modify();
-        } finally {
-            ExifInterface.closeSilently(is);
-        }
-        return true;
-    }
-
-    private void modify() {
-        mByteBuffer.order(getByteOrder());
-        for (TagOffset tagOffset : mTagOffsets) {
-            writeTagValue(tagOffset.mTag, tagOffset.mOffset);
-        }
-    }
-
-    private void writeTagValue(ExifTag tag, int offset) {
-        if (DEBUG) {
-            Log.v(TAG, "modifying tag to: \n" + tag.toString());
-            Log.v(TAG, "at offset: " + offset);
-        }
-        mByteBuffer.position(offset + mOffsetBase);
-        switch (tag.getDataType()) {
-            case ExifTag.TYPE_ASCII:
-                byte buf[] = tag.getStringByte();
-                if (buf.length == tag.getComponentCount()) {
-                    buf[buf.length - 1] = 0;
-                    mByteBuffer.put(buf);
-                } else {
-                    mByteBuffer.put(buf);
-                    mByteBuffer.put((byte) 0);
-                }
-                break;
-            case ExifTag.TYPE_LONG:
-            case ExifTag.TYPE_UNSIGNED_LONG:
-                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
-                    mByteBuffer.putInt((int) tag.getValueAt(i));
-                }
-                break;
-            case ExifTag.TYPE_RATIONAL:
-            case ExifTag.TYPE_UNSIGNED_RATIONAL:
-                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
-                    Rational v = tag.getRational(i);
-                    mByteBuffer.putInt((int) v.getNumerator());
-                    mByteBuffer.putInt((int) v.getDenominator());
-                }
-                break;
-            case ExifTag.TYPE_UNDEFINED:
-            case ExifTag.TYPE_UNSIGNED_BYTE:
-                buf = new byte[tag.getComponentCount()];
-                tag.getBytes(buf);
-                mByteBuffer.put(buf);
-                break;
-            case ExifTag.TYPE_UNSIGNED_SHORT:
-                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
-                    mByteBuffer.putShort((short) tag.getValueAt(i));
-                }
-                break;
-        }
-    }
-
-    public void modifyTag(ExifTag tag) {
-        mTagToModified.addTag(tag);
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifOutputStream.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifOutputStream.java
deleted file mode 100644
index 7ca05f2..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifOutputStream.java
+++ /dev/null
@@ -1,518 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-import android.util.Log;
-
-import java.io.BufferedOutputStream;
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.ArrayList;
-
-/**
- * This class provides a way to replace the Exif header of a JPEG image.
- * <p>
- * Below is an example of writing EXIF data into a file
- *
- * <pre>
- * public static void writeExif(byte[] jpeg, ExifData exif, String path) {
- *     OutputStream os = null;
- *     try {
- *         os = new FileOutputStream(path);
- *         ExifOutputStream eos = new ExifOutputStream(os);
- *         // Set the exif header
- *         eos.setExifData(exif);
- *         // Write the original jpeg out, the header will be add into the file.
- *         eos.write(jpeg);
- *     } catch (FileNotFoundException e) {
- *         e.printStackTrace();
- *     } catch (IOException e) {
- *         e.printStackTrace();
- *     } finally {
- *         if (os != null) {
- *             try {
- *                 os.close();
- *             } catch (IOException e) {
- *                 e.printStackTrace();
- *             }
- *         }
- *     }
- * }
- * </pre>
- */
-class ExifOutputStream extends FilterOutputStream {
-    private static final String TAG = "ExifOutputStream";
-    private static final boolean DEBUG = false;
-    private static final int STREAMBUFFER_SIZE = 0x00010000; // 64Kb
-
-    private static final int STATE_SOI = 0;
-    private static final int STATE_FRAME_HEADER = 1;
-    private static final int STATE_JPEG_DATA = 2;
-
-    private static final int EXIF_HEADER = 0x45786966;
-    private static final short TIFF_HEADER = 0x002A;
-    private static final short TIFF_BIG_ENDIAN = 0x4d4d;
-    private static final short TIFF_LITTLE_ENDIAN = 0x4949;
-    private static final short TAG_SIZE = 12;
-    private static final short TIFF_HEADER_SIZE = 8;
-    private static final int MAX_EXIF_SIZE = 65535;
-
-    private ExifData mExifData;
-    private int mState = STATE_SOI;
-    private int mByteToSkip;
-    private int mByteToCopy;
-    private byte[] mSingleByteArray = new byte[1];
-    private ByteBuffer mBuffer = ByteBuffer.allocate(4);
-    private final ExifInterface mInterface;
-
-    protected ExifOutputStream(OutputStream ou, ExifInterface iRef) {
-        super(new BufferedOutputStream(ou, STREAMBUFFER_SIZE));
-        mInterface = iRef;
-    }
-
-    /**
-     * Sets the ExifData to be written into the JPEG file. Should be called
-     * before writing image data.
-     */
-    protected void setExifData(ExifData exifData) {
-        mExifData = exifData;
-    }
-
-    /**
-     * Gets the Exif header to be written into the JPEF file.
-     */
-    protected ExifData getExifData() {
-        return mExifData;
-    }
-
-    private int requestByteToBuffer(int requestByteCount, byte[] buffer
-            , int offset, int length) {
-        int byteNeeded = requestByteCount - mBuffer.position();
-        int byteToRead = length > byteNeeded ? byteNeeded : length;
-        mBuffer.put(buffer, offset, byteToRead);
-        return byteToRead;
-    }
-
-    /**
-     * Writes the image out. The input data should be a valid JPEG format. After
-     * writing, it's Exif header will be replaced by the given header.
-     */
-    @Override
-    public void write(byte[] buffer, int offset, int length) throws IOException {
-        while ((mByteToSkip > 0 || mByteToCopy > 0 || mState != STATE_JPEG_DATA)
-                && length > 0) {
-            if (mByteToSkip > 0) {
-                int byteToProcess = length > mByteToSkip ? mByteToSkip : length;
-                length -= byteToProcess;
-                mByteToSkip -= byteToProcess;
-                offset += byteToProcess;
-            }
-            if (mByteToCopy > 0) {
-                int byteToProcess = length > mByteToCopy ? mByteToCopy : length;
-                out.write(buffer, offset, byteToProcess);
-                length -= byteToProcess;
-                mByteToCopy -= byteToProcess;
-                offset += byteToProcess;
-            }
-            if (length == 0) {
-                return;
-            }
-            switch (mState) {
-                case STATE_SOI:
-                    int byteRead = requestByteToBuffer(2, buffer, offset, length);
-                    offset += byteRead;
-                    length -= byteRead;
-                    if (mBuffer.position() < 2) {
-                        return;
-                    }
-                    mBuffer.rewind();
-                    if (mBuffer.getShort() != JpegHeader.SOI) {
-                        throw new IOException("Not a valid jpeg image, cannot write exif");
-                    }
-                    out.write(mBuffer.array(), 0, 2);
-                    mState = STATE_FRAME_HEADER;
-                    mBuffer.rewind();
-                    writeExifData();
-                    break;
-                case STATE_FRAME_HEADER:
-                    // We ignore the APP1 segment and copy all other segments
-                    // until SOF tag.
-                    byteRead = requestByteToBuffer(4, buffer, offset, length);
-                    offset += byteRead;
-                    length -= byteRead;
-                    // Check if this image data doesn't contain SOF.
-                    if (mBuffer.position() == 2) {
-                        short tag = mBuffer.getShort();
-                        if (tag == JpegHeader.EOI) {
-                            out.write(mBuffer.array(), 0, 2);
-                            mBuffer.rewind();
-                        }
-                    }
-                    if (mBuffer.position() < 4) {
-                        return;
-                    }
-                    mBuffer.rewind();
-                    short marker = mBuffer.getShort();
-                    if (marker == JpegHeader.APP1) {
-                        mByteToSkip = (mBuffer.getShort() & 0x0000ffff) - 2;
-                        mState = STATE_JPEG_DATA;
-                    } else if (!JpegHeader.isSofMarker(marker)) {
-                        out.write(mBuffer.array(), 0, 4);
-                        mByteToCopy = (mBuffer.getShort() & 0x0000ffff) - 2;
-                    } else {
-                        out.write(mBuffer.array(), 0, 4);
-                        mState = STATE_JPEG_DATA;
-                    }
-                    mBuffer.rewind();
-            }
-        }
-        if (length > 0) {
-            out.write(buffer, offset, length);
-        }
-    }
-
-    /**
-     * Writes the one bytes out. The input data should be a valid JPEG format.
-     * After writing, it's Exif header will be replaced by the given header.
-     */
-    @Override
-    public void write(int oneByte) throws IOException {
-        mSingleByteArray[0] = (byte) (0xff & oneByte);
-        write(mSingleByteArray);
-    }
-
-    /**
-     * Equivalent to calling write(buffer, 0, buffer.length).
-     */
-    @Override
-    public void write(byte[] buffer) throws IOException {
-        write(buffer, 0, buffer.length);
-    }
-
-    private void writeExifData() throws IOException {
-        if (mExifData == null) {
-            return;
-        }
-        if (DEBUG) {
-            Log.v(TAG, "Writing exif data...");
-        }
-        ArrayList<ExifTag> nullTags = stripNullValueTags(mExifData);
-        createRequiredIfdAndTag();
-        int exifSize = calculateAllOffset();
-        if (exifSize + 8 > MAX_EXIF_SIZE) {
-            throw new IOException("Exif header is too large (>64Kb)");
-        }
-        OrderedDataOutputStream dataOutputStream = new OrderedDataOutputStream(out);
-        dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
-        dataOutputStream.writeShort(JpegHeader.APP1);
-        dataOutputStream.writeShort((short) (exifSize + 8));
-        dataOutputStream.writeInt(EXIF_HEADER);
-        dataOutputStream.writeShort((short) 0x0000);
-        if (mExifData.getByteOrder() == ByteOrder.BIG_ENDIAN) {
-            dataOutputStream.writeShort(TIFF_BIG_ENDIAN);
-        } else {
-            dataOutputStream.writeShort(TIFF_LITTLE_ENDIAN);
-        }
-        dataOutputStream.setByteOrder(mExifData.getByteOrder());
-        dataOutputStream.writeShort(TIFF_HEADER);
-        dataOutputStream.writeInt(8);
-        writeAllTags(dataOutputStream);
-        writeThumbnail(dataOutputStream);
-        for (ExifTag t : nullTags) {
-            mExifData.addTag(t);
-        }
-    }
-
-    private ArrayList<ExifTag> stripNullValueTags(ExifData data) {
-        ArrayList<ExifTag> nullTags = new ArrayList<ExifTag>();
-        for(ExifTag t : data.getAllTags()) {
-            if (t.getValue() == null && !ExifInterface.isOffsetTag(t.getTagId())) {
-                data.removeTag(t.getTagId(), t.getIfd());
-                nullTags.add(t);
-            }
-        }
-        return nullTags;
-    }
-
-    private void writeThumbnail(OrderedDataOutputStream dataOutputStream) throws IOException {
-        if (mExifData.hasCompressedThumbnail()) {
-            dataOutputStream.write(mExifData.getCompressedThumbnail());
-        } else if (mExifData.hasUncompressedStrip()) {
-            for (int i = 0; i < mExifData.getStripCount(); i++) {
-                dataOutputStream.write(mExifData.getStrip(i));
-            }
-        }
-    }
-
-    private void writeAllTags(OrderedDataOutputStream dataOutputStream) throws IOException {
-        writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_0), dataOutputStream);
-        writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_EXIF), dataOutputStream);
-        IfdData interoperabilityIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
-        if (interoperabilityIfd != null) {
-            writeIfd(interoperabilityIfd, dataOutputStream);
-        }
-        IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
-        if (gpsIfd != null) {
-            writeIfd(gpsIfd, dataOutputStream);
-        }
-        IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
-        if (ifd1 != null) {
-            writeIfd(mExifData.getIfdData(IfdId.TYPE_IFD_1), dataOutputStream);
-        }
-    }
-
-    private void writeIfd(IfdData ifd, OrderedDataOutputStream dataOutputStream)
-            throws IOException {
-        ExifTag[] tags = ifd.getAllTags();
-        dataOutputStream.writeShort((short) tags.length);
-        for (ExifTag tag : tags) {
-            dataOutputStream.writeShort(tag.getTagId());
-            dataOutputStream.writeShort(tag.getDataType());
-            dataOutputStream.writeInt(tag.getComponentCount());
-            if (DEBUG) {
-                Log.v(TAG, "\n" + tag.toString());
-            }
-            if (tag.getDataSize() > 4) {
-                dataOutputStream.writeInt(tag.getOffset());
-            } else {
-                ExifOutputStream.writeTagValue(tag, dataOutputStream);
-                for (int i = 0, n = 4 - tag.getDataSize(); i < n; i++) {
-                    dataOutputStream.write(0);
-                }
-            }
-        }
-        dataOutputStream.writeInt(ifd.getOffsetToNextIfd());
-        for (ExifTag tag : tags) {
-            if (tag.getDataSize() > 4) {
-                ExifOutputStream.writeTagValue(tag, dataOutputStream);
-            }
-        }
-    }
-
-    private int calculateOffsetOfIfd(IfdData ifd, int offset) {
-        offset += 2 + ifd.getTagCount() * TAG_SIZE + 4;
-        ExifTag[] tags = ifd.getAllTags();
-        for (ExifTag tag : tags) {
-            if (tag.getDataSize() > 4) {
-                tag.setOffset(offset);
-                offset += tag.getDataSize();
-            }
-        }
-        return offset;
-    }
-
-    private void createRequiredIfdAndTag() throws IOException {
-        // IFD0 is required for all file
-        IfdData ifd0 = mExifData.getIfdData(IfdId.TYPE_IFD_0);
-        if (ifd0 == null) {
-            ifd0 = new IfdData(IfdId.TYPE_IFD_0);
-            mExifData.addIfdData(ifd0);
-        }
-        ExifTag exifOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_EXIF_IFD);
-        if (exifOffsetTag == null) {
-            throw new IOException("No definition for crucial exif tag: "
-                    + ExifInterface.TAG_EXIF_IFD);
-        }
-        ifd0.setTag(exifOffsetTag);
-
-        // Exif IFD is required for all files.
-        IfdData exifIfd = mExifData.getIfdData(IfdId.TYPE_IFD_EXIF);
-        if (exifIfd == null) {
-            exifIfd = new IfdData(IfdId.TYPE_IFD_EXIF);
-            mExifData.addIfdData(exifIfd);
-        }
-
-        // GPS IFD
-        IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
-        if (gpsIfd != null) {
-            ExifTag gpsOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_GPS_IFD);
-            if (gpsOffsetTag == null) {
-                throw new IOException("No definition for crucial exif tag: "
-                        + ExifInterface.TAG_GPS_IFD);
-            }
-            ifd0.setTag(gpsOffsetTag);
-        }
-
-        // Interoperability IFD
-        IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
-        if (interIfd != null) {
-            ExifTag interOffsetTag = mInterface
-                    .buildUninitializedTag(ExifInterface.TAG_INTEROPERABILITY_IFD);
-            if (interOffsetTag == null) {
-                throw new IOException("No definition for crucial exif tag: "
-                        + ExifInterface.TAG_INTEROPERABILITY_IFD);
-            }
-            exifIfd.setTag(interOffsetTag);
-        }
-
-        IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
-
-        // thumbnail
-        if (mExifData.hasCompressedThumbnail()) {
-
-            if (ifd1 == null) {
-                ifd1 = new IfdData(IfdId.TYPE_IFD_1);
-                mExifData.addIfdData(ifd1);
-            }
-
-            ExifTag offsetTag = mInterface
-                    .buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
-            if (offsetTag == null) {
-                throw new IOException("No definition for crucial exif tag: "
-                        + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
-            }
-
-            ifd1.setTag(offsetTag);
-            ExifTag lengthTag = mInterface
-                    .buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
-            if (lengthTag == null) {
-                throw new IOException("No definition for crucial exif tag: "
-                        + ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
-            }
-
-            lengthTag.setValue(mExifData.getCompressedThumbnail().length);
-            ifd1.setTag(lengthTag);
-
-            // Get rid of tags for uncompressed if they exist.
-            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS));
-            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS));
-        } else if (mExifData.hasUncompressedStrip()) {
-            if (ifd1 == null) {
-                ifd1 = new IfdData(IfdId.TYPE_IFD_1);
-                mExifData.addIfdData(ifd1);
-            }
-            int stripCount = mExifData.getStripCount();
-            ExifTag offsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_OFFSETS);
-            if (offsetTag == null) {
-                throw new IOException("No definition for crucial exif tag: "
-                        + ExifInterface.TAG_STRIP_OFFSETS);
-            }
-            ExifTag lengthTag = mInterface
-                    .buildUninitializedTag(ExifInterface.TAG_STRIP_BYTE_COUNTS);
-            if (lengthTag == null) {
-                throw new IOException("No definition for crucial exif tag: "
-                        + ExifInterface.TAG_STRIP_BYTE_COUNTS);
-            }
-            long[] lengths = new long[stripCount];
-            for (int i = 0; i < mExifData.getStripCount(); i++) {
-                lengths[i] = mExifData.getStrip(i).length;
-            }
-            lengthTag.setValue(lengths);
-            ifd1.setTag(offsetTag);
-            ifd1.setTag(lengthTag);
-            // Get rid of tags for compressed if they exist.
-            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT));
-            ifd1.removeTag(ExifInterface
-                    .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH));
-        } else if (ifd1 != null) {
-            // Get rid of offset and length tags if there is no thumbnail.
-            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS));
-            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS));
-            ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT));
-            ifd1.removeTag(ExifInterface
-                    .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH));
-        }
-    }
-
-    private int calculateAllOffset() {
-        int offset = TIFF_HEADER_SIZE;
-        IfdData ifd0 = mExifData.getIfdData(IfdId.TYPE_IFD_0);
-        offset = calculateOffsetOfIfd(ifd0, offset);
-        ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD)).setValue(offset);
-
-        IfdData exifIfd = mExifData.getIfdData(IfdId.TYPE_IFD_EXIF);
-        offset = calculateOffsetOfIfd(exifIfd, offset);
-
-        IfdData interIfd = mExifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY);
-        if (interIfd != null) {
-            exifIfd.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD))
-                    .setValue(offset);
-            offset = calculateOffsetOfIfd(interIfd, offset);
-        }
-
-        IfdData gpsIfd = mExifData.getIfdData(IfdId.TYPE_IFD_GPS);
-        if (gpsIfd != null) {
-            ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD)).setValue(offset);
-            offset = calculateOffsetOfIfd(gpsIfd, offset);
-        }
-
-        IfdData ifd1 = mExifData.getIfdData(IfdId.TYPE_IFD_1);
-        if (ifd1 != null) {
-            ifd0.setOffsetToNextIfd(offset);
-            offset = calculateOffsetOfIfd(ifd1, offset);
-        }
-
-        // thumbnail
-        if (mExifData.hasCompressedThumbnail()) {
-            ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT))
-                    .setValue(offset);
-            offset += mExifData.getCompressedThumbnail().length;
-        } else if (mExifData.hasUncompressedStrip()) {
-            int stripCount = mExifData.getStripCount();
-            long[] offsets = new long[stripCount];
-            for (int i = 0; i < mExifData.getStripCount(); i++) {
-                offsets[i] = offset;
-                offset += mExifData.getStrip(i).length;
-            }
-            ifd1.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)).setValue(
-                    offsets);
-        }
-        return offset;
-    }
-
-    static void writeTagValue(ExifTag tag, OrderedDataOutputStream dataOutputStream)
-            throws IOException {
-        switch (tag.getDataType()) {
-            case ExifTag.TYPE_ASCII:
-                byte buf[] = tag.getStringByte();
-                if (buf.length == tag.getComponentCount()) {
-                    buf[buf.length - 1] = 0;
-                    dataOutputStream.write(buf);
-                } else {
-                    dataOutputStream.write(buf);
-                    dataOutputStream.write(0);
-                }
-                break;
-            case ExifTag.TYPE_LONG:
-            case ExifTag.TYPE_UNSIGNED_LONG:
-                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
-                    dataOutputStream.writeInt((int) tag.getValueAt(i));
-                }
-                break;
-            case ExifTag.TYPE_RATIONAL:
-            case ExifTag.TYPE_UNSIGNED_RATIONAL:
-                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
-                    dataOutputStream.writeRational(tag.getRational(i));
-                }
-                break;
-            case ExifTag.TYPE_UNDEFINED:
-            case ExifTag.TYPE_UNSIGNED_BYTE:
-                buf = new byte[tag.getComponentCount()];
-                tag.getBytes(buf);
-                dataOutputStream.write(buf);
-                break;
-            case ExifTag.TYPE_UNSIGNED_SHORT:
-                for (int i = 0, n = tag.getComponentCount(); i < n; i++) {
-                    dataOutputStream.writeShort((short) tag.getValueAt(i));
-                }
-                break;
-        }
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifParser.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifParser.java
deleted file mode 100644
index 5467d42..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifParser.java
+++ /dev/null
@@ -1,916 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteOrder;
-import java.nio.charset.Charset;
-import java.util.Map.Entry;
-import java.util.TreeMap;
-
-/**
- * This class provides a low-level EXIF parsing API. Given a JPEG format
- * InputStream, the caller can request which IFD's to read via
- * {@link #parse(InputStream, int)} with given options.
- * <p>
- * Below is an example of getting EXIF data from IFD 0 and EXIF IFD using the
- * parser.
- *
- * <pre>
- * void parse() {
- *     ExifParser parser = ExifParser.parse(mImageInputStream,
- *             ExifParser.OPTION_IFD_0 | ExifParser.OPTIONS_IFD_EXIF);
- *     int event = parser.next();
- *     while (event != ExifParser.EVENT_END) {
- *         switch (event) {
- *             case ExifParser.EVENT_START_OF_IFD:
- *                 break;
- *             case ExifParser.EVENT_NEW_TAG:
- *                 ExifTag tag = parser.getTag();
- *                 if (!tag.hasValue()) {
- *                     parser.registerForTagValue(tag);
- *                 } else {
- *                     processTag(tag);
- *                 }
- *                 break;
- *             case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
- *                 tag = parser.getTag();
- *                 if (tag.getDataType() != ExifTag.TYPE_UNDEFINED) {
- *                     processTag(tag);
- *                 }
- *                 break;
- *         }
- *         event = parser.next();
- *     }
- * }
- *
- * void processTag(ExifTag tag) {
- *     // process the tag as you like.
- * }
- * </pre>
- */
-class ExifParser {
-    private static final boolean LOGV = false;
-    private static final String TAG = "ExifParser";
-    /**
-     * When the parser reaches a new IFD area. Call {@link #getCurrentIfd()} to
-     * know which IFD we are in.
-     */
-    public static final int EVENT_START_OF_IFD = 0;
-    /**
-     * When the parser reaches a new tag. Call {@link #getTag()}to get the
-     * corresponding tag.
-     */
-    public static final int EVENT_NEW_TAG = 1;
-    /**
-     * When the parser reaches the value area of tag that is registered by
-     * {@link #registerForTagValue(ExifTag)} previously. Call {@link #getTag()}
-     * to get the corresponding tag.
-     */
-    public static final int EVENT_VALUE_OF_REGISTERED_TAG = 2;
-
-    /**
-     * When the parser reaches the compressed image area.
-     */
-    public static final int EVENT_COMPRESSED_IMAGE = 3;
-    /**
-     * When the parser reaches the uncompressed image strip. Call
-     * {@link #getStripIndex()} to get the index of the strip.
-     *
-     * @see #getStripIndex()
-     * @see #getStripCount()
-     */
-    public static final int EVENT_UNCOMPRESSED_STRIP = 4;
-    /**
-     * When there is nothing more to parse.
-     */
-    public static final int EVENT_END = 5;
-
-    /**
-     * Option bit to request to parse IFD0.
-     */
-    public static final int OPTION_IFD_0 = 1 << 0;
-    /**
-     * Option bit to request to parse IFD1.
-     */
-    public static final int OPTION_IFD_1 = 1 << 1;
-    /**
-     * Option bit to request to parse Exif-IFD.
-     */
-    public static final int OPTION_IFD_EXIF = 1 << 2;
-    /**
-     * Option bit to request to parse GPS-IFD.
-     */
-    public static final int OPTION_IFD_GPS = 1 << 3;
-    /**
-     * Option bit to request to parse Interoperability-IFD.
-     */
-    public static final int OPTION_IFD_INTEROPERABILITY = 1 << 4;
-    /**
-     * Option bit to request to parse thumbnail.
-     */
-    public static final int OPTION_THUMBNAIL = 1 << 5;
-
-    protected static final int EXIF_HEADER = 0x45786966; // EXIF header "Exif"
-    protected static final short EXIF_HEADER_TAIL = (short) 0x0000; // EXIF header in APP1
-
-    // TIFF header
-    protected static final short LITTLE_ENDIAN_TAG = (short) 0x4949; // "II"
-    protected static final short BIG_ENDIAN_TAG = (short) 0x4d4d; // "MM"
-    protected static final short TIFF_HEADER_TAIL = 0x002A;
-
-    protected static final int TAG_SIZE = 12;
-    protected static final int OFFSET_SIZE = 2;
-
-    private static final Charset US_ASCII = Charset.forName("US-ASCII");
-
-    protected static final int DEFAULT_IFD0_OFFSET = 8;
-
-    private final CountedDataInputStream mTiffStream;
-    private final int mOptions;
-    private int mIfdStartOffset = 0;
-    private int mNumOfTagInIfd = 0;
-    private int mIfdType;
-    private ExifTag mTag;
-    private ImageEvent mImageEvent;
-    private int mStripCount;
-    private ExifTag mStripSizeTag;
-    private ExifTag mJpegSizeTag;
-    private boolean mNeedToParseOffsetsInCurrentIfd;
-    private boolean mContainExifData = false;
-    private int mApp1End;
-    private int mOffsetToApp1EndFromSOF = 0;
-    private byte[] mDataAboveIfd0;
-    private int mIfd0Position;
-    private int mTiffStartPosition;
-    private final ExifInterface mInterface;
-
-    private static final short TAG_EXIF_IFD = ExifInterface
-            .getTrueTagKey(ExifInterface.TAG_EXIF_IFD);
-    private static final short TAG_GPS_IFD = ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD);
-    private static final short TAG_INTEROPERABILITY_IFD = ExifInterface
-            .getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD);
-    private static final short TAG_JPEG_INTERCHANGE_FORMAT = ExifInterface
-            .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT);
-    private static final short TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = ExifInterface
-            .getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH);
-    private static final short TAG_STRIP_OFFSETS = ExifInterface
-            .getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS);
-    private static final short TAG_STRIP_BYTE_COUNTS = ExifInterface
-            .getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS);
-
-    private final TreeMap<Integer, Object> mCorrespondingEvent = new TreeMap<Integer, Object>();
-
-    private boolean isIfdRequested(int ifdType) {
-        switch (ifdType) {
-            case IfdId.TYPE_IFD_0:
-                return (mOptions & OPTION_IFD_0) != 0;
-            case IfdId.TYPE_IFD_1:
-                return (mOptions & OPTION_IFD_1) != 0;
-            case IfdId.TYPE_IFD_EXIF:
-                return (mOptions & OPTION_IFD_EXIF) != 0;
-            case IfdId.TYPE_IFD_GPS:
-                return (mOptions & OPTION_IFD_GPS) != 0;
-            case IfdId.TYPE_IFD_INTEROPERABILITY:
-                return (mOptions & OPTION_IFD_INTEROPERABILITY) != 0;
-        }
-        return false;
-    }
-
-    private boolean isThumbnailRequested() {
-        return (mOptions & OPTION_THUMBNAIL) != 0;
-    }
-
-    private ExifParser(InputStream inputStream, int options, ExifInterface iRef)
-            throws IOException, ExifInvalidFormatException {
-        if (inputStream == null) {
-            throw new IOException("Null argument inputStream to ExifParser");
-        }
-        if (LOGV) {
-            Log.v(TAG, "Reading exif...");
-        }
-        mInterface = iRef;
-        mContainExifData = seekTiffData(inputStream);
-        mTiffStream = new CountedDataInputStream(inputStream);
-        mOptions = options;
-        if (!mContainExifData) {
-            return;
-        }
-
-        parseTiffHeader();
-        long offset = mTiffStream.readUnsignedInt();
-        if (offset > Integer.MAX_VALUE) {
-            throw new ExifInvalidFormatException("Invalid offset " + offset);
-        }
-        mIfd0Position = (int) offset;
-        mIfdType = IfdId.TYPE_IFD_0;
-        if (isIfdRequested(IfdId.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) {
-            registerIfd(IfdId.TYPE_IFD_0, offset);
-            if (offset != DEFAULT_IFD0_OFFSET) {
-                mDataAboveIfd0 = new byte[(int) offset - DEFAULT_IFD0_OFFSET];
-                read(mDataAboveIfd0);
-            }
-        }
-    }
-
-    /**
-     * Parses the the given InputStream with the given options
-     *
-     * @exception IOException
-     * @exception ExifInvalidFormatException
-     */
-    protected static ExifParser parse(InputStream inputStream, int options, ExifInterface iRef)
-            throws IOException, ExifInvalidFormatException {
-        return new ExifParser(inputStream, options, iRef);
-    }
-
-    /**
-     * Parses the the given InputStream with default options; that is, every IFD
-     * and thumbnaill will be parsed.
-     *
-     * @exception IOException
-     * @exception ExifInvalidFormatException
-     * @see #parse(InputStream, int)
-     */
-    protected static ExifParser parse(InputStream inputStream, ExifInterface iRef)
-            throws IOException, ExifInvalidFormatException {
-        return new ExifParser(inputStream, OPTION_IFD_0 | OPTION_IFD_1
-                | OPTION_IFD_EXIF | OPTION_IFD_GPS | OPTION_IFD_INTEROPERABILITY
-                | OPTION_THUMBNAIL, iRef);
-    }
-
-    /**
-     * Moves the parser forward and returns the next parsing event
-     *
-     * @exception IOException
-     * @exception ExifInvalidFormatException
-     * @see #EVENT_START_OF_IFD
-     * @see #EVENT_NEW_TAG
-     * @see #EVENT_VALUE_OF_REGISTERED_TAG
-     * @see #EVENT_COMPRESSED_IMAGE
-     * @see #EVENT_UNCOMPRESSED_STRIP
-     * @see #EVENT_END
-     */
-    protected int next() throws IOException, ExifInvalidFormatException {
-        if (!mContainExifData) {
-            return EVENT_END;
-        }
-        int offset = mTiffStream.getReadByteCount();
-        int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
-        if (offset < endOfTags) {
-            mTag = readTag();
-            if (mTag == null) {
-                return next();
-            }
-            if (mNeedToParseOffsetsInCurrentIfd) {
-                checkOffsetOrImageTag(mTag);
-            }
-            return EVENT_NEW_TAG;
-        } else if (offset == endOfTags) {
-            // There is a link to ifd1 at the end of ifd0
-            if (mIfdType == IfdId.TYPE_IFD_0) {
-                long ifdOffset = readUnsignedLong();
-                if (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested()) {
-                    if (ifdOffset != 0) {
-                        registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
-                    }
-                }
-            } else {
-                int offsetSize = 4;
-                // Some camera models use invalid length of the offset
-                if (mCorrespondingEvent.size() > 0) {
-                    offsetSize = mCorrespondingEvent.firstEntry().getKey() -
-                            mTiffStream.getReadByteCount();
-                }
-                if (offsetSize < 4) {
-                    Log.w(TAG, "Invalid size of link to next IFD: " + offsetSize);
-                } else {
-                    long ifdOffset = readUnsignedLong();
-                    if (ifdOffset != 0) {
-                        Log.w(TAG, "Invalid link to next IFD: " + ifdOffset);
-                    }
-                }
-            }
-        }
-        while (mCorrespondingEvent.size() != 0) {
-            Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
-            Object event = entry.getValue();
-            try {
-                skipTo(entry.getKey());
-            } catch (IOException e) {
-                Log.w(TAG, "Failed to skip to data at: " + entry.getKey() +
-                        " for " + event.getClass().getName() + ", the file may be broken.");
-                continue;
-            }
-            if (event instanceof IfdEvent) {
-                mIfdType = ((IfdEvent) event).ifd;
-                mNumOfTagInIfd = mTiffStream.readUnsignedShort();
-                mIfdStartOffset = entry.getKey();
-
-                if (mNumOfTagInIfd * TAG_SIZE + mIfdStartOffset + OFFSET_SIZE > mApp1End) {
-                    Log.w(TAG, "Invalid size of IFD " + mIfdType);
-                    return EVENT_END;
-                }
-
-                mNeedToParseOffsetsInCurrentIfd = needToParseOffsetsInCurrentIfd();
-                if (((IfdEvent) event).isRequested) {
-                    return EVENT_START_OF_IFD;
-                } else {
-                    skipRemainingTagsInCurrentIfd();
-                }
-            } else if (event instanceof ImageEvent) {
-                mImageEvent = (ImageEvent) event;
-                return mImageEvent.type;
-            } else {
-                ExifTagEvent tagEvent = (ExifTagEvent) event;
-                mTag = tagEvent.tag;
-                if (mTag.getDataType() != ExifTag.TYPE_UNDEFINED) {
-                    readFullTagValue(mTag);
-                    checkOffsetOrImageTag(mTag);
-                }
-                if (tagEvent.isRequested) {
-                    return EVENT_VALUE_OF_REGISTERED_TAG;
-                }
-            }
-        }
-        return EVENT_END;
-    }
-
-    /**
-     * Skips the tags area of current IFD, if the parser is not in the tag area,
-     * nothing will happen.
-     *
-     * @throws IOException
-     * @throws ExifInvalidFormatException
-     */
-    protected void skipRemainingTagsInCurrentIfd() throws IOException, ExifInvalidFormatException {
-        int endOfTags = mIfdStartOffset + OFFSET_SIZE + TAG_SIZE * mNumOfTagInIfd;
-        int offset = mTiffStream.getReadByteCount();
-        if (offset > endOfTags) {
-            return;
-        }
-        if (mNeedToParseOffsetsInCurrentIfd) {
-            while (offset < endOfTags) {
-                mTag = readTag();
-                offset += TAG_SIZE;
-                if (mTag == null) {
-                    continue;
-                }
-                checkOffsetOrImageTag(mTag);
-            }
-        } else {
-            skipTo(endOfTags);
-        }
-        long ifdOffset = readUnsignedLong();
-        // For ifd0, there is a link to ifd1 in the end of all tags
-        if (mIfdType == IfdId.TYPE_IFD_0
-                && (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested())) {
-            if (ifdOffset > 0) {
-                registerIfd(IfdId.TYPE_IFD_1, ifdOffset);
-            }
-        }
-    }
-
-    private boolean needToParseOffsetsInCurrentIfd() {
-        switch (mIfdType) {
-            case IfdId.TYPE_IFD_0:
-                return isIfdRequested(IfdId.TYPE_IFD_EXIF) || isIfdRequested(IfdId.TYPE_IFD_GPS)
-                        || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)
-                        || isIfdRequested(IfdId.TYPE_IFD_1);
-            case IfdId.TYPE_IFD_1:
-                return isThumbnailRequested();
-            case IfdId.TYPE_IFD_EXIF:
-                // The offset to interoperability IFD is located in Exif IFD
-                return isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY);
-            default:
-                return false;
-        }
-    }
-
-    /**
-     * If {@link #next()} return {@link #EVENT_NEW_TAG} or
-     * {@link #EVENT_VALUE_OF_REGISTERED_TAG}, call this function to get the
-     * corresponding tag.
-     * <p>
-     * For {@link #EVENT_NEW_TAG}, the tag may not contain the value if the size
-     * of the value is greater than 4 bytes. One should call
-     * {@link ExifTag#hasValue()} to check if the tag contains value. If there
-     * is no value,call {@link #registerForTagValue(ExifTag)} to have the parser
-     * emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
-     * pointed by the offset.
-     * <p>
-     * When {@link #EVENT_VALUE_OF_REGISTERED_TAG} is emitted, the value of the
-     * tag will have already been read except for tags of undefined type. For
-     * tags of undefined type, call one of the read methods to get the value.
-     *
-     * @see #registerForTagValue(ExifTag)
-     * @see #read(byte[])
-     * @see #read(byte[], int, int)
-     * @see #readLong()
-     * @see #readRational()
-     * @see #readString(int)
-     * @see #readString(int, Charset)
-     */
-    protected ExifTag getTag() {
-        return mTag;
-    }
-
-    /**
-     * Gets number of tags in the current IFD area.
-     */
-    protected int getTagCountInCurrentIfd() {
-        return mNumOfTagInIfd;
-    }
-
-    /**
-     * Gets the ID of current IFD.
-     *
-     * @see IfdId#TYPE_IFD_0
-     * @see IfdId#TYPE_IFD_1
-     * @see IfdId#TYPE_IFD_GPS
-     * @see IfdId#TYPE_IFD_INTEROPERABILITY
-     * @see IfdId#TYPE_IFD_EXIF
-     */
-    protected int getCurrentIfd() {
-        return mIfdType;
-    }
-
-    /**
-     * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
-     * get the index of this strip.
-     *
-     * @see #getStripCount()
-     */
-    protected int getStripIndex() {
-        return mImageEvent.stripIndex;
-    }
-
-    /**
-     * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
-     * get the number of strip data.
-     *
-     * @see #getStripIndex()
-     */
-    protected int getStripCount() {
-        return mStripCount;
-    }
-
-    /**
-     * When receiving {@link #EVENT_UNCOMPRESSED_STRIP}, call this function to
-     * get the strip size.
-     */
-    protected int getStripSize() {
-        if (mStripSizeTag == null)
-            return 0;
-        return (int) mStripSizeTag.getValueAt(0);
-    }
-
-    /**
-     * When receiving {@link #EVENT_COMPRESSED_IMAGE}, call this function to get
-     * the image data size.
-     */
-    protected int getCompressedImageSize() {
-        if (mJpegSizeTag == null) {
-            return 0;
-        }
-        return (int) mJpegSizeTag.getValueAt(0);
-    }
-
-    private void skipTo(int offset) throws IOException {
-        mTiffStream.skipTo(offset);
-        while (!mCorrespondingEvent.isEmpty() && mCorrespondingEvent.firstKey() < offset) {
-            mCorrespondingEvent.pollFirstEntry();
-        }
-    }
-
-    /**
-     * When getting {@link #EVENT_NEW_TAG} in the tag area of IFD, the tag may
-     * not contain the value if the size of the value is greater than 4 bytes.
-     * When the value is not available here, call this method so that the parser
-     * will emit {@link #EVENT_VALUE_OF_REGISTERED_TAG} when it reaches the area
-     * where the value is located.
-     *
-     * @see #EVENT_VALUE_OF_REGISTERED_TAG
-     */
-    protected void registerForTagValue(ExifTag tag) {
-        if (tag.getOffset() >= mTiffStream.getReadByteCount()) {
-            mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, true));
-        }
-    }
-
-    private void registerIfd(int ifdType, long offset) {
-        // Cast unsigned int to int since the offset is always smaller
-        // than the size of APP1 (65536)
-        mCorrespondingEvent.put((int) offset, new IfdEvent(ifdType, isIfdRequested(ifdType)));
-    }
-
-    private void registerCompressedImage(long offset) {
-        mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_COMPRESSED_IMAGE));
-    }
-
-    private void registerUncompressedStrip(int stripIndex, long offset) {
-        mCorrespondingEvent.put((int) offset, new ImageEvent(EVENT_UNCOMPRESSED_STRIP
-                , stripIndex));
-    }
-
-    private ExifTag readTag() throws IOException, ExifInvalidFormatException {
-        short tagId = mTiffStream.readShort();
-        short dataFormat = mTiffStream.readShort();
-        long numOfComp = mTiffStream.readUnsignedInt();
-        if (numOfComp > Integer.MAX_VALUE) {
-            throw new ExifInvalidFormatException(
-                    "Number of component is larger then Integer.MAX_VALUE");
-        }
-        // Some invalid image file contains invalid data type. Ignore those tags
-        if (!ExifTag.isValidType(dataFormat)) {
-            Log.w(TAG, String.format("Tag %04x: Invalid data type %d", tagId, dataFormat));
-            mTiffStream.skip(4);
-            return null;
-        }
-        // TODO: handle numOfComp overflow
-        ExifTag tag = new ExifTag(tagId, dataFormat, (int) numOfComp, mIfdType,
-                ((int) numOfComp) != ExifTag.SIZE_UNDEFINED);
-        int dataSize = tag.getDataSize();
-        if (dataSize > 4) {
-            long offset = mTiffStream.readUnsignedInt();
-            if (offset > Integer.MAX_VALUE) {
-                throw new ExifInvalidFormatException(
-                        "offset is larger then Integer.MAX_VALUE");
-            }
-            // Some invalid images put some undefined data before IFD0.
-            // Read the data here.
-            if ((offset < mIfd0Position) && (dataFormat == ExifTag.TYPE_UNDEFINED)) {
-                byte[] buf = new byte[(int) numOfComp];
-                System.arraycopy(mDataAboveIfd0, (int) offset - DEFAULT_IFD0_OFFSET,
-                        buf, 0, (int) numOfComp);
-                tag.setValue(buf);
-            } else {
-                tag.setOffset((int) offset);
-            }
-        } else {
-            boolean defCount = tag.hasDefinedCount();
-            // Set defined count to 0 so we can add \0 to non-terminated strings
-            tag.setHasDefinedCount(false);
-            // Read value
-            readFullTagValue(tag);
-            tag.setHasDefinedCount(defCount);
-            mTiffStream.skip(4 - dataSize);
-            // Set the offset to the position of value.
-            tag.setOffset(mTiffStream.getReadByteCount() - 4);
-        }
-        return tag;
-    }
-
-    /**
-     * Check the tag, if the tag is one of the offset tag that points to the IFD
-     * or image the caller is interested in, register the IFD or image.
-     */
-    private void checkOffsetOrImageTag(ExifTag tag) {
-        // Some invalid formattd image contains tag with 0 size.
-        if (tag.getComponentCount() == 0) {
-            return;
-        }
-        short tid = tag.getTagId();
-        int ifd = tag.getIfd();
-        if (tid == TAG_EXIF_IFD && checkAllowed(ifd, ExifInterface.TAG_EXIF_IFD)) {
-            if (isIfdRequested(IfdId.TYPE_IFD_EXIF)
-                    || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
-                registerIfd(IfdId.TYPE_IFD_EXIF, tag.getValueAt(0));
-            }
-        } else if (tid == TAG_GPS_IFD && checkAllowed(ifd, ExifInterface.TAG_GPS_IFD)) {
-            if (isIfdRequested(IfdId.TYPE_IFD_GPS)) {
-                registerIfd(IfdId.TYPE_IFD_GPS, tag.getValueAt(0));
-            }
-        } else if (tid == TAG_INTEROPERABILITY_IFD
-                && checkAllowed(ifd, ExifInterface.TAG_INTEROPERABILITY_IFD)) {
-            if (isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
-                registerIfd(IfdId.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0));
-            }
-        } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT
-                && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) {
-            if (isThumbnailRequested()) {
-                registerCompressedImage(tag.getValueAt(0));
-            }
-        } else if (tid == TAG_JPEG_INTERCHANGE_FORMAT_LENGTH
-                && checkAllowed(ifd, ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)) {
-            if (isThumbnailRequested()) {
-                mJpegSizeTag = tag;
-            }
-        } else if (tid == TAG_STRIP_OFFSETS && checkAllowed(ifd, ExifInterface.TAG_STRIP_OFFSETS)) {
-            if (isThumbnailRequested()) {
-                if (tag.hasValue()) {
-                    for (int i = 0; i < tag.getComponentCount(); i++) {
-                        if (tag.getDataType() == ExifTag.TYPE_UNSIGNED_SHORT) {
-                            registerUncompressedStrip(i, tag.getValueAt(i));
-                        } else {
-                            registerUncompressedStrip(i, tag.getValueAt(i));
-                        }
-                    }
-                } else {
-                    mCorrespondingEvent.put(tag.getOffset(), new ExifTagEvent(tag, false));
-                }
-            }
-        } else if (tid == TAG_STRIP_BYTE_COUNTS
-                && checkAllowed(ifd, ExifInterface.TAG_STRIP_BYTE_COUNTS)
-                &&isThumbnailRequested() && tag.hasValue()) {
-            mStripSizeTag = tag;
-        }
-    }
-
-    private boolean checkAllowed(int ifd, int tagId) {
-        int info = mInterface.getTagInfo().get(tagId);
-        if (info == ExifInterface.DEFINITION_NULL) {
-            return false;
-        }
-        return ExifInterface.isIfdAllowed(info, ifd);
-    }
-
-    protected void readFullTagValue(ExifTag tag) throws IOException {
-        // Some invalid images contains tags with wrong size, check it here
-        short type = tag.getDataType();
-        if (type == ExifTag.TYPE_ASCII || type == ExifTag.TYPE_UNDEFINED ||
-                type == ExifTag.TYPE_UNSIGNED_BYTE) {
-            int size = tag.getComponentCount();
-            if (mCorrespondingEvent.size() > 0) {
-                if (mCorrespondingEvent.firstEntry().getKey() < mTiffStream.getReadByteCount()
-                        + size) {
-                    Object event = mCorrespondingEvent.firstEntry().getValue();
-                    if (event instanceof ImageEvent) {
-                        // Tag value overlaps thumbnail, ignore thumbnail.
-                        Log.w(TAG, "Thumbnail overlaps value for tag: \n" + tag.toString());
-                        Entry<Integer, Object> entry = mCorrespondingEvent.pollFirstEntry();
-                        Log.w(TAG, "Invalid thumbnail offset: " + entry.getKey());
-                    } else {
-                        // Tag value overlaps another tag, shorten count
-                        if (event instanceof IfdEvent) {
-                            Log.w(TAG, "Ifd " + ((IfdEvent) event).ifd
-                                    + " overlaps value for tag: \n" + tag.toString());
-                        } else if (event instanceof ExifTagEvent) {
-                            Log.w(TAG, "Tag value for tag: \n"
-                                    + ((ExifTagEvent) event).tag.toString()
-                                    + " overlaps value for tag: \n" + tag.toString());
-                        }
-                        size = mCorrespondingEvent.firstEntry().getKey()
-                                - mTiffStream.getReadByteCount();
-                        Log.w(TAG, "Invalid size of tag: \n" + tag.toString()
-                                + " setting count to: " + size);
-                        tag.forceSetComponentCount(size);
-                    }
-                }
-            }
-        }
-        switch (tag.getDataType()) {
-            case ExifTag.TYPE_UNSIGNED_BYTE:
-            case ExifTag.TYPE_UNDEFINED: {
-                byte buf[] = new byte[tag.getComponentCount()];
-                read(buf);
-                tag.setValue(buf);
-            }
-                break;
-            case ExifTag.TYPE_ASCII:
-                tag.setValue(readString(tag.getComponentCount()));
-                break;
-            case ExifTag.TYPE_UNSIGNED_LONG: {
-                long value[] = new long[tag.getComponentCount()];
-                for (int i = 0, n = value.length; i < n; i++) {
-                    value[i] = readUnsignedLong();
-                }
-                tag.setValue(value);
-            }
-                break;
-            case ExifTag.TYPE_UNSIGNED_RATIONAL: {
-                Rational value[] = new Rational[tag.getComponentCount()];
-                for (int i = 0, n = value.length; i < n; i++) {
-                    value[i] = readUnsignedRational();
-                }
-                tag.setValue(value);
-            }
-                break;
-            case ExifTag.TYPE_UNSIGNED_SHORT: {
-                int value[] = new int[tag.getComponentCount()];
-                for (int i = 0, n = value.length; i < n; i++) {
-                    value[i] = readUnsignedShort();
-                }
-                tag.setValue(value);
-            }
-                break;
-            case ExifTag.TYPE_LONG: {
-                int value[] = new int[tag.getComponentCount()];
-                for (int i = 0, n = value.length; i < n; i++) {
-                    value[i] = readLong();
-                }
-                tag.setValue(value);
-            }
-                break;
-            case ExifTag.TYPE_RATIONAL: {
-                Rational value[] = new Rational[tag.getComponentCount()];
-                for (int i = 0, n = value.length; i < n; i++) {
-                    value[i] = readRational();
-                }
-                tag.setValue(value);
-            }
-                break;
-        }
-        if (LOGV) {
-            Log.v(TAG, "\n" + tag.toString());
-        }
-    }
-
-    private void parseTiffHeader() throws IOException,
-            ExifInvalidFormatException {
-        short byteOrder = mTiffStream.readShort();
-        if (LITTLE_ENDIAN_TAG == byteOrder) {
-            mTiffStream.setByteOrder(ByteOrder.LITTLE_ENDIAN);
-        } else if (BIG_ENDIAN_TAG == byteOrder) {
-            mTiffStream.setByteOrder(ByteOrder.BIG_ENDIAN);
-        } else {
-            throw new ExifInvalidFormatException("Invalid TIFF header");
-        }
-
-        if (mTiffStream.readShort() != TIFF_HEADER_TAIL) {
-            throw new ExifInvalidFormatException("Invalid TIFF header");
-        }
-    }
-
-    private boolean seekTiffData(InputStream inputStream) throws IOException,
-            ExifInvalidFormatException {
-        CountedDataInputStream dataStream = new CountedDataInputStream(inputStream);
-        if (dataStream.readShort() != JpegHeader.SOI) {
-            throw new ExifInvalidFormatException("Invalid JPEG format");
-        }
-
-        short marker = dataStream.readShort();
-        while (marker != JpegHeader.EOI
-                && !JpegHeader.isSofMarker(marker)) {
-            int length = dataStream.readUnsignedShort();
-            // Some invalid formatted image contains multiple APP1,
-            // try to find the one with Exif data.
-            if (marker == JpegHeader.APP1) {
-                int header = 0;
-                short headerTail = 0;
-                if (length >= 8) {
-                    header = dataStream.readInt();
-                    headerTail = dataStream.readShort();
-                    length -= 6;
-                    if (header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) {
-                        mTiffStartPosition = dataStream.getReadByteCount();
-                        mApp1End = length;
-                        mOffsetToApp1EndFromSOF = mTiffStartPosition + mApp1End;
-                        return true;
-                    }
-                }
-            }
-            if (length < 2 || (length - 2) != dataStream.skip(length - 2)) {
-                Log.w(TAG, "Invalid JPEG format.");
-                return false;
-            }
-            marker = dataStream.readShort();
-        }
-        return false;
-    }
-
-    protected int getOffsetToExifEndFromSOF() {
-        return mOffsetToApp1EndFromSOF;
-    }
-
-    protected int getTiffStartPosition() {
-        return mTiffStartPosition;
-    }
-
-    /**
-     * Reads bytes from the InputStream.
-     */
-    protected int read(byte[] buffer, int offset, int length) throws IOException {
-        return mTiffStream.read(buffer, offset, length);
-    }
-
-    /**
-     * Equivalent to read(buffer, 0, buffer.length).
-     */
-    protected int read(byte[] buffer) throws IOException {
-        return mTiffStream.read(buffer);
-    }
-
-    /**
-     * Reads a String from the InputStream with US-ASCII charset. The parser
-     * will read n bytes and convert it to ascii string. This is used for
-     * reading values of type {@link ExifTag#TYPE_ASCII}.
-     */
-    protected String readString(int n) throws IOException {
-        return readString(n, US_ASCII);
-    }
-
-    /**
-     * Reads a String from the InputStream with the given charset. The parser
-     * will read n bytes and convert it to string. This is used for reading
-     * values of type {@link ExifTag#TYPE_ASCII}.
-     */
-    protected String readString(int n, Charset charset) throws IOException {
-        if (n > 0) {
-            return mTiffStream.readString(n, charset);
-        } else {
-            return "";
-        }
-    }
-
-    /**
-     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_SHORT} from the
-     * InputStream.
-     */
-    protected int readUnsignedShort() throws IOException {
-        return mTiffStream.readShort() & 0xffff;
-    }
-
-    /**
-     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_LONG} from the
-     * InputStream.
-     */
-    protected long readUnsignedLong() throws IOException {
-        return readLong() & 0xffffffffL;
-    }
-
-    /**
-     * Reads value of type {@link ExifTag#TYPE_UNSIGNED_RATIONAL} from the
-     * InputStream.
-     */
-    protected Rational readUnsignedRational() throws IOException {
-        long nomi = readUnsignedLong();
-        long denomi = readUnsignedLong();
-        return new Rational(nomi, denomi);
-    }
-
-    /**
-     * Reads value of type {@link ExifTag#TYPE_LONG} from the InputStream.
-     */
-    protected int readLong() throws IOException {
-        return mTiffStream.readInt();
-    }
-
-    /**
-     * Reads value of type {@link ExifTag#TYPE_RATIONAL} from the InputStream.
-     */
-    protected Rational readRational() throws IOException {
-        int nomi = readLong();
-        int denomi = readLong();
-        return new Rational(nomi, denomi);
-    }
-
-    private static class ImageEvent {
-        int stripIndex;
-        int type;
-
-        ImageEvent(int type) {
-            this.stripIndex = 0;
-            this.type = type;
-        }
-
-        ImageEvent(int type, int stripIndex) {
-            this.type = type;
-            this.stripIndex = stripIndex;
-        }
-    }
-
-    private static class IfdEvent {
-        int ifd;
-        boolean isRequested;
-
-        IfdEvent(int ifd, boolean isInterestedIfd) {
-            this.ifd = ifd;
-            this.isRequested = isInterestedIfd;
-        }
-    }
-
-    private static class ExifTagEvent {
-        ExifTag tag;
-        boolean isRequested;
-
-        ExifTagEvent(ExifTag tag, boolean isRequireByUser) {
-            this.tag = tag;
-            this.isRequested = isRequireByUser;
-        }
-    }
-
-    /**
-     * Gets the byte order of the current InputStream.
-     */
-    protected ByteOrder getByteOrder() {
-        return mTiffStream.getByteOrder();
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifReader.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifReader.java
deleted file mode 100644
index 68e972f..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifReader.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-/**
- * This class reads the EXIF header of a JPEG file and stores it in
- * {@link ExifData}.
- */
-class ExifReader {
-    private static final String TAG = "ExifReader";
-
-    private final ExifInterface mInterface;
-
-    ExifReader(ExifInterface iRef) {
-        mInterface = iRef;
-    }
-
-    /**
-     * Parses the inputStream and and returns the EXIF data in an
-     * {@link ExifData}.
-     *
-     * @throws ExifInvalidFormatException
-     * @throws IOException
-     */
-    protected ExifData read(InputStream inputStream) throws ExifInvalidFormatException,
-            IOException {
-        ExifParser parser = ExifParser.parse(inputStream, mInterface);
-        ExifData exifData = new ExifData(parser.getByteOrder());
-        ExifTag tag = null;
-
-        int event = parser.next();
-        while (event != ExifParser.EVENT_END) {
-            switch (event) {
-                case ExifParser.EVENT_START_OF_IFD:
-                    exifData.addIfdData(new IfdData(parser.getCurrentIfd()));
-                    break;
-                case ExifParser.EVENT_NEW_TAG:
-                    tag = parser.getTag();
-                    if (!tag.hasValue()) {
-                        parser.registerForTagValue(tag);
-                    } else {
-                        exifData.getIfdData(tag.getIfd()).setTag(tag);
-                    }
-                    break;
-                case ExifParser.EVENT_VALUE_OF_REGISTERED_TAG:
-                    tag = parser.getTag();
-                    if (tag.getDataType() == ExifTag.TYPE_UNDEFINED) {
-                        parser.readFullTagValue(tag);
-                    }
-                    exifData.getIfdData(tag.getIfd()).setTag(tag);
-                    break;
-                case ExifParser.EVENT_COMPRESSED_IMAGE:
-                    byte buf[] = new byte[parser.getCompressedImageSize()];
-                    if (buf.length == parser.read(buf)) {
-                        exifData.setCompressedThumbnail(buf);
-                    } else {
-                        Log.w(TAG, "Failed to read the compressed thumbnail");
-                    }
-                    break;
-                case ExifParser.EVENT_UNCOMPRESSED_STRIP:
-                    buf = new byte[parser.getStripSize()];
-                    if (buf.length == parser.read(buf)) {
-                        exifData.setStripBytes(parser.getStripIndex(), buf);
-                    } else {
-                        Log.w(TAG, "Failed to read the strip bytes");
-                    }
-                    break;
-            }
-            event = parser.next();
-        }
-        return exifData;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifTag.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifTag.java
deleted file mode 100644
index b8b3872..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifTag.java
+++ /dev/null
@@ -1,1008 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-import java.nio.charset.Charset;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Date;
-
-/**
- * This class stores information of an EXIF tag. For more information about
- * defined EXIF tags, please read the Jeita EXIF 2.2 standard. Tags should be
- * instantiated using {@link ExifInterface#buildTag}.
- *
- * @see ExifInterface
- */
-public class ExifTag {
-    /**
-     * The BYTE type in the EXIF standard. An 8-bit unsigned integer.
-     */
-    public static final short TYPE_UNSIGNED_BYTE = 1;
-    /**
-     * The ASCII type in the EXIF standard. An 8-bit byte containing one 7-bit
-     * ASCII code. The final byte is terminated with NULL.
-     */
-    public static final short TYPE_ASCII = 2;
-    /**
-     * The SHORT type in the EXIF standard. A 16-bit (2-byte) unsigned integer
-     */
-    public static final short TYPE_UNSIGNED_SHORT = 3;
-    /**
-     * The LONG type in the EXIF standard. A 32-bit (4-byte) unsigned integer
-     */
-    public static final short TYPE_UNSIGNED_LONG = 4;
-    /**
-     * The RATIONAL type of EXIF standard. It consists of two LONGs. The first
-     * one is the numerator and the second one expresses the denominator.
-     */
-    public static final short TYPE_UNSIGNED_RATIONAL = 5;
-    /**
-     * The UNDEFINED type in the EXIF standard. An 8-bit byte that can take any
-     * value depending on the field definition.
-     */
-    public static final short TYPE_UNDEFINED = 7;
-    /**
-     * The SLONG type in the EXIF standard. A 32-bit (4-byte) signed integer
-     * (2's complement notation).
-     */
-    public static final short TYPE_LONG = 9;
-    /**
-     * The SRATIONAL type of EXIF standard. It consists of two SLONGs. The first
-     * one is the numerator and the second one is the denominator.
-     */
-    public static final short TYPE_RATIONAL = 10;
-
-    private static Charset US_ASCII = Charset.forName("US-ASCII");
-    private static final int TYPE_TO_SIZE_MAP[] = new int[11];
-    private static final int UNSIGNED_SHORT_MAX = 65535;
-    private static final long UNSIGNED_LONG_MAX = 4294967295L;
-    private static final long LONG_MAX = Integer.MAX_VALUE;
-    private static final long LONG_MIN = Integer.MIN_VALUE;
-
-    static {
-        TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_BYTE] = 1;
-        TYPE_TO_SIZE_MAP[TYPE_ASCII] = 1;
-        TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_SHORT] = 2;
-        TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_LONG] = 4;
-        TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_RATIONAL] = 8;
-        TYPE_TO_SIZE_MAP[TYPE_UNDEFINED] = 1;
-        TYPE_TO_SIZE_MAP[TYPE_LONG] = 4;
-        TYPE_TO_SIZE_MAP[TYPE_RATIONAL] = 8;
-    }
-
-    static final int SIZE_UNDEFINED = 0;
-
-    // Exif TagId
-    private final short mTagId;
-    // Exif Tag Type
-    private final short mDataType;
-    // If tag has defined count
-    private boolean mHasDefinedDefaultComponentCount;
-    // Actual data count in tag (should be number of elements in value array)
-    private int mComponentCountActual;
-    // The ifd that this tag should be put in
-    private int mIfd;
-    // The value (array of elements of type Tag Type)
-    private Object mValue;
-    // Value offset in exif header.
-    private int mOffset;
-
-    private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy:MM:dd kk:mm:ss");
-
-    /**
-     * Returns true if the given IFD is a valid IFD.
-     */
-    public static boolean isValidIfd(int ifdId) {
-        return ifdId == IfdId.TYPE_IFD_0 || ifdId == IfdId.TYPE_IFD_1
-                || ifdId == IfdId.TYPE_IFD_EXIF || ifdId == IfdId.TYPE_IFD_INTEROPERABILITY
-                || ifdId == IfdId.TYPE_IFD_GPS;
-    }
-
-    /**
-     * Returns true if a given type is a valid tag type.
-     */
-    public static boolean isValidType(short type) {
-        return type == TYPE_UNSIGNED_BYTE || type == TYPE_ASCII ||
-                type == TYPE_UNSIGNED_SHORT || type == TYPE_UNSIGNED_LONG ||
-                type == TYPE_UNSIGNED_RATIONAL || type == TYPE_UNDEFINED ||
-                type == TYPE_LONG || type == TYPE_RATIONAL;
-    }
-
-    // Use builtTag in ExifInterface instead of constructor.
-    ExifTag(short tagId, short type, int componentCount, int ifd,
-            boolean hasDefinedComponentCount) {
-        mTagId = tagId;
-        mDataType = type;
-        mComponentCountActual = componentCount;
-        mHasDefinedDefaultComponentCount = hasDefinedComponentCount;
-        mIfd = ifd;
-        mValue = null;
-    }
-
-    /**
-     * Gets the element size of the given data type in bytes.
-     *
-     * @see #TYPE_ASCII
-     * @see #TYPE_LONG
-     * @see #TYPE_RATIONAL
-     * @see #TYPE_UNDEFINED
-     * @see #TYPE_UNSIGNED_BYTE
-     * @see #TYPE_UNSIGNED_LONG
-     * @see #TYPE_UNSIGNED_RATIONAL
-     * @see #TYPE_UNSIGNED_SHORT
-     */
-    public static int getElementSize(short type) {
-        return TYPE_TO_SIZE_MAP[type];
-    }
-
-    /**
-     * Returns the ID of the IFD this tag belongs to.
-     *
-     * @see IfdId#TYPE_IFD_0
-     * @see IfdId#TYPE_IFD_1
-     * @see IfdId#TYPE_IFD_EXIF
-     * @see IfdId#TYPE_IFD_GPS
-     * @see IfdId#TYPE_IFD_INTEROPERABILITY
-     */
-    public int getIfd() {
-        return mIfd;
-    }
-
-    protected void setIfd(int ifdId) {
-        mIfd = ifdId;
-    }
-
-    /**
-     * Gets the TID of this tag.
-     */
-    public short getTagId() {
-        return mTagId;
-    }
-
-    /**
-     * Gets the data type of this tag
-     *
-     * @see #TYPE_ASCII
-     * @see #TYPE_LONG
-     * @see #TYPE_RATIONAL
-     * @see #TYPE_UNDEFINED
-     * @see #TYPE_UNSIGNED_BYTE
-     * @see #TYPE_UNSIGNED_LONG
-     * @see #TYPE_UNSIGNED_RATIONAL
-     * @see #TYPE_UNSIGNED_SHORT
-     */
-    public short getDataType() {
-        return mDataType;
-    }
-
-    /**
-     * Gets the total data size in bytes of the value of this tag.
-     */
-    public int getDataSize() {
-        return getComponentCount() * getElementSize(getDataType());
-    }
-
-    /**
-     * Gets the component count of this tag.
-     */
-
-    // TODO: fix integer overflows with this
-    public int getComponentCount() {
-        return mComponentCountActual;
-    }
-
-    /**
-     * Sets the component count of this tag. Call this function before
-     * setValue() if the length of value does not match the component count.
-     */
-    protected void forceSetComponentCount(int count) {
-        mComponentCountActual = count;
-    }
-
-    /**
-     * Returns true if this ExifTag contains value; otherwise, this tag will
-     * contain an offset value that is determined when the tag is written.
-     */
-    public boolean hasValue() {
-        return mValue != null;
-    }
-
-    /**
-     * Sets integer values into this tag. This method should be used for tags of
-     * type {@link #TYPE_UNSIGNED_SHORT}. This method will fail if:
-     * <ul>
-     * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_SHORT},
-     * {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_LONG}.</li>
-     * <li>The value overflows.</li>
-     * <li>The value.length does NOT match the component count in the definition
-     * for this tag.</li>
-     * </ul>
-     */
-    public boolean setValue(int[] value) {
-        if (checkBadComponentCount(value.length)) {
-            return false;
-        }
-        if (mDataType != TYPE_UNSIGNED_SHORT && mDataType != TYPE_LONG &&
-                mDataType != TYPE_UNSIGNED_LONG) {
-            return false;
-        }
-        if (mDataType == TYPE_UNSIGNED_SHORT && checkOverflowForUnsignedShort(value)) {
-            return false;
-        } else if (mDataType == TYPE_UNSIGNED_LONG && checkOverflowForUnsignedLong(value)) {
-            return false;
-        }
-
-        long[] data = new long[value.length];
-        for (int i = 0; i < value.length; i++) {
-            data[i] = value[i];
-        }
-        mValue = data;
-        mComponentCountActual = value.length;
-        return true;
-    }
-
-    /**
-     * Sets integer value into this tag. This method should be used for tags of
-     * type {@link #TYPE_UNSIGNED_SHORT}, or {@link #TYPE_LONG}. This method
-     * will fail if:
-     * <ul>
-     * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_SHORT},
-     * {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_LONG}.</li>
-     * <li>The value overflows.</li>
-     * <li>The component count in the definition of this tag is not 1.</li>
-     * </ul>
-     */
-    public boolean setValue(int value) {
-        return setValue(new int[] {
-                value
-        });
-    }
-
-    /**
-     * Sets long values into this tag. This method should be used for tags of
-     * type {@link #TYPE_UNSIGNED_LONG}. This method will fail if:
-     * <ul>
-     * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_LONG}.</li>
-     * <li>The value overflows.</li>
-     * <li>The value.length does NOT match the component count in the definition
-     * for this tag.</li>
-     * </ul>
-     */
-    public boolean setValue(long[] value) {
-        if (checkBadComponentCount(value.length) || mDataType != TYPE_UNSIGNED_LONG) {
-            return false;
-        }
-        if (checkOverflowForUnsignedLong(value)) {
-            return false;
-        }
-        mValue = value;
-        mComponentCountActual = value.length;
-        return true;
-    }
-
-    /**
-     * Sets long values into this tag. This method should be used for tags of
-     * type {@link #TYPE_UNSIGNED_LONG}. This method will fail if:
-     * <ul>
-     * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_LONG}.</li>
-     * <li>The value overflows.</li>
-     * <li>The component count in the definition for this tag is not 1.</li>
-     * </ul>
-     */
-    public boolean setValue(long value) {
-        return setValue(new long[] {
-                value
-        });
-    }
-
-    /**
-     * Sets a string value into this tag. This method should be used for tags of
-     * type {@link #TYPE_ASCII}. The string is converted to an ASCII string.
-     * Characters that cannot be converted are replaced with '?'. The length of
-     * the string must be equal to either (component count -1) or (component
-     * count). The final byte will be set to the string null terminator '\0',
-     * overwriting the last character in the string if the value.length is equal
-     * to the component count. This method will fail if:
-     * <ul>
-     * <li>The data type is not {@link #TYPE_ASCII} or {@link #TYPE_UNDEFINED}.</li>
-     * <li>The length of the string is not equal to (component count -1) or
-     * (component count) in the definition for this tag.</li>
-     * </ul>
-     */
-    public boolean setValue(String value) {
-        if (mDataType != TYPE_ASCII && mDataType != TYPE_UNDEFINED) {
-            return false;
-        }
-
-        byte[] buf = value.getBytes(US_ASCII);
-        byte[] finalBuf = buf;
-        if (buf.length > 0) {
-            finalBuf = (buf[buf.length - 1] == 0 || mDataType == TYPE_UNDEFINED) ? buf : Arrays
-                .copyOf(buf, buf.length + 1);
-        } else if (mDataType == TYPE_ASCII && mComponentCountActual == 1) {
-            finalBuf = new byte[] { 0 };
-        }
-        int count = finalBuf.length;
-        if (checkBadComponentCount(count)) {
-            return false;
-        }
-        mComponentCountActual = count;
-        mValue = finalBuf;
-        return true;
-    }
-
-    /**
-     * Sets Rational values into this tag. This method should be used for tags
-     * of type {@link #TYPE_UNSIGNED_RATIONAL}, or {@link #TYPE_RATIONAL}. This
-     * method will fail if:
-     * <ul>
-     * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_RATIONAL}
-     * or {@link #TYPE_RATIONAL}.</li>
-     * <li>The value overflows.</li>
-     * <li>The value.length does NOT match the component count in the definition
-     * for this tag.</li>
-     * </ul>
-     *
-     * @see Rational
-     */
-    public boolean setValue(Rational[] value) {
-        if (checkBadComponentCount(value.length)) {
-            return false;
-        }
-        if (mDataType != TYPE_UNSIGNED_RATIONAL && mDataType != TYPE_RATIONAL) {
-            return false;
-        }
-        if (mDataType == TYPE_UNSIGNED_RATIONAL && checkOverflowForUnsignedRational(value)) {
-            return false;
-        } else if (mDataType == TYPE_RATIONAL && checkOverflowForRational(value)) {
-            return false;
-        }
-
-        mValue = value;
-        mComponentCountActual = value.length;
-        return true;
-    }
-
-    /**
-     * Sets a Rational value into this tag. This method should be used for tags
-     * of type {@link #TYPE_UNSIGNED_RATIONAL}, or {@link #TYPE_RATIONAL}. This
-     * method will fail if:
-     * <ul>
-     * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_RATIONAL}
-     * or {@link #TYPE_RATIONAL}.</li>
-     * <li>The value overflows.</li>
-     * <li>The component count in the definition for this tag is not 1.</li>
-     * </ul>
-     *
-     * @see Rational
-     */
-    public boolean setValue(Rational value) {
-        return setValue(new Rational[] {
-                value
-        });
-    }
-
-    /**
-     * Sets byte values into this tag. This method should be used for tags of
-     * type {@link #TYPE_UNSIGNED_BYTE} or {@link #TYPE_UNDEFINED}. This method
-     * will fail if:
-     * <ul>
-     * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_BYTE} or
-     * {@link #TYPE_UNDEFINED} .</li>
-     * <li>The length does NOT match the component count in the definition for
-     * this tag.</li>
-     * </ul>
-     */
-    public boolean setValue(byte[] value, int offset, int length) {
-        if (checkBadComponentCount(length)) {
-            return false;
-        }
-        if (mDataType != TYPE_UNSIGNED_BYTE && mDataType != TYPE_UNDEFINED) {
-            return false;
-        }
-        mValue = new byte[length];
-        System.arraycopy(value, offset, mValue, 0, length);
-        mComponentCountActual = length;
-        return true;
-    }
-
-    /**
-     * Equivalent to setValue(value, 0, value.length).
-     */
-    public boolean setValue(byte[] value) {
-        return setValue(value, 0, value.length);
-    }
-
-    /**
-     * Sets byte value into this tag. This method should be used for tags of
-     * type {@link #TYPE_UNSIGNED_BYTE} or {@link #TYPE_UNDEFINED}. This method
-     * will fail if:
-     * <ul>
-     * <li>The component type of this tag is not {@link #TYPE_UNSIGNED_BYTE} or
-     * {@link #TYPE_UNDEFINED} .</li>
-     * <li>The component count in the definition for this tag is not 1.</li>
-     * </ul>
-     */
-    public boolean setValue(byte value) {
-        return setValue(new byte[] {
-                value
-        });
-    }
-
-    /**
-     * Sets the value for this tag using an appropriate setValue method for the
-     * given object. This method will fail if:
-     * <ul>
-     * <li>The corresponding setValue method for the class of the object passed
-     * in would fail.</li>
-     * <li>There is no obvious way to cast the object passed in into an EXIF tag
-     * type.</li>
-     * </ul>
-     */
-    public boolean setValue(Object obj) {
-        if (obj == null) {
-            return false;
-        } else if (obj instanceof Short) {
-            return setValue(((Short) obj).shortValue() & 0x0ffff);
-        } else if (obj instanceof String) {
-            return setValue((String) obj);
-        } else if (obj instanceof int[]) {
-            return setValue((int[]) obj);
-        } else if (obj instanceof long[]) {
-            return setValue((long[]) obj);
-        } else if (obj instanceof Rational) {
-            return setValue((Rational) obj);
-        } else if (obj instanceof Rational[]) {
-            return setValue((Rational[]) obj);
-        } else if (obj instanceof byte[]) {
-            return setValue((byte[]) obj);
-        } else if (obj instanceof Integer) {
-            return setValue(((Integer) obj).intValue());
-        } else if (obj instanceof Long) {
-            return setValue(((Long) obj).longValue());
-        } else if (obj instanceof Byte) {
-            return setValue(((Byte) obj).byteValue());
-        } else if (obj instanceof Short[]) {
-            // Nulls in this array are treated as zeroes.
-            Short[] arr = (Short[]) obj;
-            int[] fin = new int[arr.length];
-            for (int i = 0; i < arr.length; i++) {
-                fin[i] = (arr[i] == null) ? 0 : arr[i].shortValue() & 0x0ffff;
-            }
-            return setValue(fin);
-        } else if (obj instanceof Integer[]) {
-            // Nulls in this array are treated as zeroes.
-            Integer[] arr = (Integer[]) obj;
-            int[] fin = new int[arr.length];
-            for (int i = 0; i < arr.length; i++) {
-                fin[i] = (arr[i] == null) ? 0 : arr[i].intValue();
-            }
-            return setValue(fin);
-        } else if (obj instanceof Long[]) {
-            // Nulls in this array are treated as zeroes.
-            Long[] arr = (Long[]) obj;
-            long[] fin = new long[arr.length];
-            for (int i = 0; i < arr.length; i++) {
-                fin[i] = (arr[i] == null) ? 0 : arr[i].longValue();
-            }
-            return setValue(fin);
-        } else if (obj instanceof Byte[]) {
-            // Nulls in this array are treated as zeroes.
-            Byte[] arr = (Byte[]) obj;
-            byte[] fin = new byte[arr.length];
-            for (int i = 0; i < arr.length; i++) {
-                fin[i] = (arr[i] == null) ? 0 : arr[i].byteValue();
-            }
-            return setValue(fin);
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Sets a timestamp to this tag. The method converts the timestamp with the
-     * format of "yyyy:MM:dd kk:mm:ss" and calls {@link #setValue(String)}. This
-     * method will fail if the data type is not {@link #TYPE_ASCII} or the
-     * component count of this tag is not 20 or undefined.
-     *
-     * @param time the number of milliseconds since Jan. 1, 1970 GMT
-     * @return true on success
-     */
-    public boolean setTimeValue(long time) {
-        // synchronized on TIME_FORMAT as SimpleDateFormat is not thread safe
-        synchronized (TIME_FORMAT) {
-            return setValue(TIME_FORMAT.format(new Date(time)));
-        }
-    }
-
-    /**
-     * Gets the value as a String. This method should be used for tags of type
-     * {@link #TYPE_ASCII}.
-     *
-     * @return the value as a String, or null if the tag's value does not exist
-     *         or cannot be converted to a String.
-     */
-    public String getValueAsString() {
-        if (mValue == null) {
-            return null;
-        } else if (mValue instanceof String) {
-            return (String) mValue;
-        } else if (mValue instanceof byte[]) {
-            return new String((byte[]) mValue, US_ASCII);
-        }
-        return null;
-    }
-
-    /**
-     * Gets the value as a String. This method should be used for tags of type
-     * {@link #TYPE_ASCII}.
-     *
-     * @param defaultValue the String to return if the tag's value does not
-     *            exist or cannot be converted to a String.
-     * @return the tag's value as a String, or the defaultValue.
-     */
-    public String getValueAsString(String defaultValue) {
-        String s = getValueAsString();
-        if (s == null) {
-            return defaultValue;
-        }
-        return s;
-    }
-
-    /**
-     * Gets the value as a byte array. This method should be used for tags of
-     * type {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}.
-     *
-     * @return the value as a byte array, or null if the tag's value does not
-     *         exist or cannot be converted to a byte array.
-     */
-    public byte[] getValueAsBytes() {
-        if (mValue instanceof byte[]) {
-            return (byte[]) mValue;
-        }
-        return null;
-    }
-
-    /**
-     * Gets the value as a byte. If there are more than 1 bytes in this value,
-     * gets the first byte. This method should be used for tags of type
-     * {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}.
-     *
-     * @param defaultValue the byte to return if tag's value does not exist or
-     *            cannot be converted to a byte.
-     * @return the tag's value as a byte, or the defaultValue.
-     */
-    public byte getValueAsByte(byte defaultValue) {
-        byte[] b = getValueAsBytes();
-        if (b == null || b.length < 1) {
-            return defaultValue;
-        }
-        return b[0];
-    }
-
-    /**
-     * Gets the value as an array of Rationals. This method should be used for
-     * tags of type {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
-     *
-     * @return the value as as an array of Rationals, or null if the tag's value
-     *         does not exist or cannot be converted to an array of Rationals.
-     */
-    public Rational[] getValueAsRationals() {
-        if (mValue instanceof Rational[]) {
-            return (Rational[]) mValue;
-        }
-        return null;
-    }
-
-    /**
-     * Gets the value as a Rational. If there are more than 1 Rationals in this
-     * value, gets the first one. This method should be used for tags of type
-     * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
-     *
-     * @param defaultValue the Rational to return if tag's value does not exist
-     *            or cannot be converted to a Rational.
-     * @return the tag's value as a Rational, or the defaultValue.
-     */
-    public Rational getValueAsRational(Rational defaultValue) {
-        Rational[] r = getValueAsRationals();
-        if (r == null || r.length < 1) {
-            return defaultValue;
-        }
-        return r[0];
-    }
-
-    /**
-     * Gets the value as a Rational. If there are more than 1 Rationals in this
-     * value, gets the first one. This method should be used for tags of type
-     * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
-     *
-     * @param defaultValue the numerator of the Rational to return if tag's
-     *            value does not exist or cannot be converted to a Rational (the
-     *            denominator will be 1).
-     * @return the tag's value as a Rational, or the defaultValue.
-     */
-    public Rational getValueAsRational(long defaultValue) {
-        Rational defaultVal = new Rational(defaultValue, 1);
-        return getValueAsRational(defaultVal);
-    }
-
-    /**
-     * Gets the value as an array of ints. This method should be used for tags
-     * of type {@link #TYPE_UNSIGNED_SHORT}, {@link #TYPE_UNSIGNED_LONG}.
-     *
-     * @return the value as as an array of ints, or null if the tag's value does
-     *         not exist or cannot be converted to an array of ints.
-     */
-    public int[] getValueAsInts() {
-        if (mValue == null) {
-            return null;
-        } else if (mValue instanceof long[]) {
-            long[] val = (long[]) mValue;
-            int[] arr = new int[val.length];
-            for (int i = 0; i < val.length; i++) {
-                arr[i] = (int) val[i]; // Truncates
-            }
-            return arr;
-        }
-        return null;
-    }
-
-    /**
-     * Gets the value as an int. If there are more than 1 ints in this value,
-     * gets the first one. This method should be used for tags of type
-     * {@link #TYPE_UNSIGNED_SHORT}, {@link #TYPE_UNSIGNED_LONG}.
-     *
-     * @param defaultValue the int to return if tag's value does not exist or
-     *            cannot be converted to an int.
-     * @return the tag's value as a int, or the defaultValue.
-     */
-    public int getValueAsInt(int defaultValue) {
-        int[] i = getValueAsInts();
-        if (i == null || i.length < 1) {
-            return defaultValue;
-        }
-        return i[0];
-    }
-
-    /**
-     * Gets the value as an array of longs. This method should be used for tags
-     * of type {@link #TYPE_UNSIGNED_LONG}.
-     *
-     * @return the value as as an array of longs, or null if the tag's value
-     *         does not exist or cannot be converted to an array of longs.
-     */
-    public long[] getValueAsLongs() {
-        if (mValue instanceof long[]) {
-            return (long[]) mValue;
-        }
-        return null;
-    }
-
-    /**
-     * Gets the value or null if none exists. If there are more than 1 longs in
-     * this value, gets the first one. This method should be used for tags of
-     * type {@link #TYPE_UNSIGNED_LONG}.
-     *
-     * @param defaultValue the long to return if tag's value does not exist or
-     *            cannot be converted to a long.
-     * @return the tag's value as a long, or the defaultValue.
-     */
-    public long getValueAsLong(long defaultValue) {
-        long[] l = getValueAsLongs();
-        if (l == null || l.length < 1) {
-            return defaultValue;
-        }
-        return l[0];
-    }
-
-    /**
-     * Gets the tag's value or null if none exists.
-     */
-    public Object getValue() {
-        return mValue;
-    }
-
-    /**
-     * Gets a long representation of the value.
-     *
-     * @param defaultValue value to return if there is no value or value is a
-     *            rational with a denominator of 0.
-     * @return the tag's value as a long, or defaultValue if no representation
-     *         exists.
-     */
-    public long forceGetValueAsLong(long defaultValue) {
-        long[] l = getValueAsLongs();
-        if (l != null && l.length >= 1) {
-            return l[0];
-        }
-        byte[] b = getValueAsBytes();
-        if (b != null && b.length >= 1) {
-            return b[0];
-        }
-        Rational[] r = getValueAsRationals();
-        if (r != null && r.length >= 1 && r[0].getDenominator() != 0) {
-            return (long) r[0].toDouble();
-        }
-        return defaultValue;
-    }
-
-    /**
-     * Gets a string representation of the value.
-     */
-    public String forceGetValueAsString() {
-        if (mValue == null) {
-            return "";
-        } else if (mValue instanceof byte[]) {
-            if (mDataType == TYPE_ASCII) {
-                return new String((byte[]) mValue, US_ASCII);
-            } else {
-                return Arrays.toString((byte[]) mValue);
-            }
-        } else if (mValue instanceof long[]) {
-            if (((long[]) mValue).length == 1) {
-                return String.valueOf(((long[]) mValue)[0]);
-            } else {
-                return Arrays.toString((long[]) mValue);
-            }
-        } else if (mValue instanceof Object[]) {
-            if (((Object[]) mValue).length == 1) {
-                Object val = ((Object[]) mValue)[0];
-                if (val == null) {
-                    return "";
-                } else {
-                    return val.toString();
-                }
-            } else {
-                return Arrays.toString((Object[]) mValue);
-            }
-        } else {
-            return mValue.toString();
-        }
-    }
-
-    /**
-     * Gets the value for type {@link #TYPE_ASCII}, {@link #TYPE_LONG},
-     * {@link #TYPE_UNDEFINED}, {@link #TYPE_UNSIGNED_BYTE},
-     * {@link #TYPE_UNSIGNED_LONG}, or {@link #TYPE_UNSIGNED_SHORT}. For
-     * {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}, call
-     * {@link #getRational(int)} instead.
-     *
-     * @exception IllegalArgumentException if the data type is
-     *                {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
-     */
-    protected long getValueAt(int index) {
-        if (mValue instanceof long[]) {
-            return ((long[]) mValue)[index];
-        } else if (mValue instanceof byte[]) {
-            return ((byte[]) mValue)[index];
-        }
-        throw new IllegalArgumentException("Cannot get integer value from "
-                + convertTypeToString(mDataType));
-    }
-
-    /**
-     * Gets the {@link #TYPE_ASCII} data.
-     *
-     * @exception IllegalArgumentException If the type is NOT
-     *                {@link #TYPE_ASCII}.
-     */
-    protected String getString() {
-        if (mDataType != TYPE_ASCII) {
-            throw new IllegalArgumentException("Cannot get ASCII value from "
-                    + convertTypeToString(mDataType));
-        }
-        return new String((byte[]) mValue, US_ASCII);
-    }
-
-    /*
-     * Get the converted ascii byte. Used by ExifOutputStream.
-     */
-    protected byte[] getStringByte() {
-        return (byte[]) mValue;
-    }
-
-    /**
-     * Gets the {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL} data.
-     *
-     * @exception IllegalArgumentException If the type is NOT
-     *                {@link #TYPE_RATIONAL} or {@link #TYPE_UNSIGNED_RATIONAL}.
-     */
-    protected Rational getRational(int index) {
-        if ((mDataType != TYPE_RATIONAL) && (mDataType != TYPE_UNSIGNED_RATIONAL)) {
-            throw new IllegalArgumentException("Cannot get RATIONAL value from "
-                    + convertTypeToString(mDataType));
-        }
-        return ((Rational[]) mValue)[index];
-    }
-
-    /**
-     * Equivalent to getBytes(buffer, 0, buffer.length).
-     */
-    protected void getBytes(byte[] buf) {
-        getBytes(buf, 0, buf.length);
-    }
-
-    /**
-     * Gets the {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE} data.
-     *
-     * @param buf the byte array in which to store the bytes read.
-     * @param offset the initial position in buffer to store the bytes.
-     * @param length the maximum number of bytes to store in buffer. If length >
-     *            component count, only the valid bytes will be stored.
-     * @exception IllegalArgumentException If the type is NOT
-     *                {@link #TYPE_UNDEFINED} or {@link #TYPE_UNSIGNED_BYTE}.
-     */
-    protected void getBytes(byte[] buf, int offset, int length) {
-        if ((mDataType != TYPE_UNDEFINED) && (mDataType != TYPE_UNSIGNED_BYTE)) {
-            throw new IllegalArgumentException("Cannot get BYTE value from "
-                    + convertTypeToString(mDataType));
-        }
-        System.arraycopy(mValue, 0, buf, offset,
-                (length > mComponentCountActual) ? mComponentCountActual : length);
-    }
-
-    /**
-     * Gets the offset of this tag. This is only valid if this data size > 4 and
-     * contains an offset to the location of the actual value.
-     */
-    protected int getOffset() {
-        return mOffset;
-    }
-
-    /**
-     * Sets the offset of this tag.
-     */
-    protected void setOffset(int offset) {
-        mOffset = offset;
-    }
-
-    protected void setHasDefinedCount(boolean d) {
-        mHasDefinedDefaultComponentCount = d;
-    }
-
-    protected boolean hasDefinedCount() {
-        return mHasDefinedDefaultComponentCount;
-    }
-
-    private boolean checkBadComponentCount(int count) {
-        if (mHasDefinedDefaultComponentCount && (mComponentCountActual != count)) {
-            return true;
-        }
-        return false;
-    }
-
-    private static String convertTypeToString(short type) {
-        switch (type) {
-            case TYPE_UNSIGNED_BYTE:
-                return "UNSIGNED_BYTE";
-            case TYPE_ASCII:
-                return "ASCII";
-            case TYPE_UNSIGNED_SHORT:
-                return "UNSIGNED_SHORT";
-            case TYPE_UNSIGNED_LONG:
-                return "UNSIGNED_LONG";
-            case TYPE_UNSIGNED_RATIONAL:
-                return "UNSIGNED_RATIONAL";
-            case TYPE_UNDEFINED:
-                return "UNDEFINED";
-            case TYPE_LONG:
-                return "LONG";
-            case TYPE_RATIONAL:
-                return "RATIONAL";
-            default:
-                return "";
-        }
-    }
-
-    private boolean checkOverflowForUnsignedShort(int[] value) {
-        for (int v : value) {
-            if (v > UNSIGNED_SHORT_MAX || v < 0) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean checkOverflowForUnsignedLong(long[] value) {
-        for (long v : value) {
-            if (v < 0 || v > UNSIGNED_LONG_MAX) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean checkOverflowForUnsignedLong(int[] value) {
-        for (int v : value) {
-            if (v < 0) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean checkOverflowForUnsignedRational(Rational[] value) {
-        for (Rational v : value) {
-            if (v.getNumerator() < 0 || v.getDenominator() < 0
-                    || v.getNumerator() > UNSIGNED_LONG_MAX
-                    || v.getDenominator() > UNSIGNED_LONG_MAX) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean checkOverflowForRational(Rational[] value) {
-        for (Rational v : value) {
-            if (v.getNumerator() < LONG_MIN || v.getDenominator() < LONG_MIN
-                    || v.getNumerator() > LONG_MAX
-                    || v.getDenominator() > LONG_MAX) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (obj instanceof ExifTag) {
-            ExifTag tag = (ExifTag) obj;
-            if (tag.mTagId != this.mTagId
-                    || tag.mComponentCountActual != this.mComponentCountActual
-                    || tag.mDataType != this.mDataType) {
-                return false;
-            }
-            if (mValue != null) {
-                if (tag.mValue == null) {
-                    return false;
-                } else if (mValue instanceof long[]) {
-                    if (!(tag.mValue instanceof long[])) {
-                        return false;
-                    }
-                    return Arrays.equals((long[]) mValue, (long[]) tag.mValue);
-                } else if (mValue instanceof Rational[]) {
-                    if (!(tag.mValue instanceof Rational[])) {
-                        return false;
-                    }
-                    return Arrays.equals((Rational[]) mValue, (Rational[]) tag.mValue);
-                } else if (mValue instanceof byte[]) {
-                    if (!(tag.mValue instanceof byte[])) {
-                        return false;
-                    }
-                    return Arrays.equals((byte[]) mValue, (byte[]) tag.mValue);
-                } else {
-                    return mValue.equals(tag.mValue);
-                }
-            } else {
-                return tag.mValue == null;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("tag id: %04X\n", mTagId) + "ifd id: " + mIfd + "\ntype: "
-                + convertTypeToString(mDataType) + "\ncount: " + mComponentCountActual
-                + "\noffset: " + mOffset + "\nvalue: " + forceGetValueAsString() + "\n";
-    }
-
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/IfdData.java b/WallpaperPicker/src/com/android/gallery3d/exif/IfdData.java
deleted file mode 100644
index 093944a..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/IfdData.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * This class stores all the tags in an IFD.
- *
- * @see ExifData
- * @see ExifTag
- */
-class IfdData {
-
-    private final int mIfdId;
-    private final Map<Short, ExifTag> mExifTags = new HashMap<Short, ExifTag>();
-    private int mOffsetToNextIfd = 0;
-    private static final int[] sIfds = {
-            IfdId.TYPE_IFD_0, IfdId.TYPE_IFD_1, IfdId.TYPE_IFD_EXIF,
-            IfdId.TYPE_IFD_INTEROPERABILITY, IfdId.TYPE_IFD_GPS
-    };
-    /**
-     * Creates an IfdData with given IFD ID.
-     *
-     * @see IfdId#TYPE_IFD_0
-     * @see IfdId#TYPE_IFD_1
-     * @see IfdId#TYPE_IFD_EXIF
-     * @see IfdId#TYPE_IFD_GPS
-     * @see IfdId#TYPE_IFD_INTEROPERABILITY
-     */
-    IfdData(int ifdId) {
-        mIfdId = ifdId;
-    }
-
-    static protected int[] getIfds() {
-        return sIfds;
-    }
-
-    /**
-     * Get a array the contains all {@link ExifTag} in this IFD.
-     */
-    protected ExifTag[] getAllTags() {
-        return mExifTags.values().toArray(new ExifTag[mExifTags.size()]);
-    }
-
-    /**
-     * Gets the ID of this IFD.
-     *
-     * @see IfdId#TYPE_IFD_0
-     * @see IfdId#TYPE_IFD_1
-     * @see IfdId#TYPE_IFD_EXIF
-     * @see IfdId#TYPE_IFD_GPS
-     * @see IfdId#TYPE_IFD_INTEROPERABILITY
-     */
-    protected int getId() {
-        return mIfdId;
-    }
-
-    /**
-     * Gets the {@link ExifTag} with given tag id. Return null if there is no
-     * such tag.
-     */
-    protected ExifTag getTag(short tagId) {
-        return mExifTags.get(tagId);
-    }
-
-    /**
-     * Adds or replaces a {@link ExifTag}.
-     */
-    protected ExifTag setTag(ExifTag tag) {
-        tag.setIfd(mIfdId);
-        return mExifTags.put(tag.getTagId(), tag);
-    }
-
-    protected boolean checkCollision(short tagId) {
-        return mExifTags.get(tagId) != null;
-    }
-
-    /**
-     * Removes the tag of the given ID
-     */
-    protected void removeTag(short tagId) {
-        mExifTags.remove(tagId);
-    }
-
-    /**
-     * Gets the tags count in the IFD.
-     */
-    protected int getTagCount() {
-        return mExifTags.size();
-    }
-
-    /**
-     * Sets the offset of next IFD.
-     */
-    protected void setOffsetToNextIfd(int offset) {
-        mOffsetToNextIfd = offset;
-    }
-
-    /**
-     * Gets the offset of next IFD.
-     */
-    protected int getOffsetToNextIfd() {
-        return mOffsetToNextIfd;
-    }
-
-    /**
-     * Returns true if all tags in this two IFDs are equal. Note that tags of
-     * IFDs offset or thumbnail offset will be ignored.
-     */
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (obj instanceof IfdData) {
-            IfdData data = (IfdData) obj;
-            if (data.getId() == mIfdId && data.getTagCount() == getTagCount()) {
-                ExifTag[] tags = data.getAllTags();
-                for (ExifTag tag : tags) {
-                    if (ExifInterface.isOffsetTag(tag.getTagId())) {
-                        continue;
-                    }
-                    ExifTag tag2 = mExifTags.get(tag.getTagId());
-                    if (!tag.equals(tag2)) {
-                        return false;
-                    }
-                }
-                return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/IfdId.java b/WallpaperPicker/src/com/android/gallery3d/exif/IfdId.java
deleted file mode 100644
index 7842edb..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/IfdId.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-/**
- * The constants of the IFD ID defined in EXIF spec.
- */
-public interface IfdId {
-    public static final int TYPE_IFD_0 = 0;
-    public static final int TYPE_IFD_1 = 1;
-    public static final int TYPE_IFD_EXIF = 2;
-    public static final int TYPE_IFD_INTEROPERABILITY = 3;
-    public static final int TYPE_IFD_GPS = 4;
-    /* This is used in ExifData to allocate enough IfdData */
-    static final int TYPE_IFD_COUNT = 5;
-
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/JpegHeader.java b/WallpaperPicker/src/com/android/gallery3d/exif/JpegHeader.java
deleted file mode 100644
index e3e787e..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/JpegHeader.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-class JpegHeader {
-    public static final short SOI =  (short) 0xFFD8;
-    public static final short APP1 = (short) 0xFFE1;
-    public static final short APP0 = (short) 0xFFE0;
-    public static final short EOI = (short) 0xFFD9;
-
-    /**
-     *  SOF (start of frame). All value between SOF0 and SOF15 is SOF marker except for DHT, JPG,
-     *  and DAC marker.
-     */
-    public static final short SOF0 = (short) 0xFFC0;
-    public static final short SOF15 = (short) 0xFFCF;
-    public static final short DHT = (short) 0xFFC4;
-    public static final short JPG = (short) 0xFFC8;
-    public static final short DAC = (short) 0xFFCC;
-
-    public static final boolean isSofMarker(short marker) {
-        return marker >= SOF0 && marker <= SOF15 && marker != DHT && marker != JPG
-                && marker != DAC;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/OrderedDataOutputStream.java b/WallpaperPicker/src/com/android/gallery3d/exif/OrderedDataOutputStream.java
deleted file mode 100644
index 428e6b9..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/OrderedDataOutputStream.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-class OrderedDataOutputStream extends FilterOutputStream {
-    private final ByteBuffer mByteBuffer = ByteBuffer.allocate(4);
-
-    public OrderedDataOutputStream(OutputStream out) {
-        super(out);
-    }
-
-    public OrderedDataOutputStream setByteOrder(ByteOrder order) {
-        mByteBuffer.order(order);
-        return this;
-    }
-
-    public OrderedDataOutputStream writeShort(short value) throws IOException {
-        mByteBuffer.rewind();
-        mByteBuffer.putShort(value);
-        out.write(mByteBuffer.array(), 0, 2);
-        return this;
-    }
-
-    public OrderedDataOutputStream writeInt(int value) throws IOException {
-        mByteBuffer.rewind();
-        mByteBuffer.putInt(value);
-        out.write(mByteBuffer.array());
-        return this;
-    }
-
-    public OrderedDataOutputStream writeRational(Rational rational) throws IOException {
-        writeInt((int) rational.getNumerator());
-        writeInt((int) rational.getDenominator());
-        return this;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/Rational.java b/WallpaperPicker/src/com/android/gallery3d/exif/Rational.java
deleted file mode 100644
index 591d63f..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/exif/Rational.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.exif;
-
-/**
- * The rational data type of EXIF tag. Contains a pair of longs representing the
- * numerator and denominator of a Rational number.
- */
-public class Rational {
-
-    private final long mNumerator;
-    private final long mDenominator;
-
-    /**
-     * Create a Rational with a given numerator and denominator.
-     *
-     * @param nominator
-     * @param denominator
-     */
-    public Rational(long nominator, long denominator) {
-        mNumerator = nominator;
-        mDenominator = denominator;
-    }
-
-    /**
-     * Create a copy of a Rational.
-     */
-    public Rational(Rational r) {
-        mNumerator = r.mNumerator;
-        mDenominator = r.mDenominator;
-    }
-
-    /**
-     * Gets the numerator of the rational.
-     */
-    public long getNumerator() {
-        return mNumerator;
-    }
-
-    /**
-     * Gets the denominator of the rational
-     */
-    public long getDenominator() {
-        return mDenominator;
-    }
-
-    /**
-     * Gets the rational value as type double. Will cause a divide-by-zero error
-     * if the denominator is 0.
-     */
-    public double toDouble() {
-        return mNumerator / (double) mDenominator;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (this == obj) {
-            return true;
-        }
-        if (obj instanceof Rational) {
-            Rational data = (Rational) obj;
-            return mNumerator == data.mNumerator && mDenominator == data.mDenominator;
-        }
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        return mNumerator + "/" + mDenominator;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/BasicTexture.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/BasicTexture.java
deleted file mode 100644
index 0f3efb7..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/BasicTexture.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.glrenderer;
-
-import android.util.Log;
-
-import com.android.gallery3d.common.Utils;
-
-import java.util.WeakHashMap;
-
-// BasicTexture is a Texture corresponds to a real GL texture.
-// The state of a BasicTexture indicates whether its data is loaded to GL memory.
-// If a BasicTexture is loaded into GL memory, it has a GL texture id.
-public abstract class BasicTexture implements Texture {
-
-    private static final String TAG = "BasicTexture";
-    protected static final int UNSPECIFIED = -1;
-
-    protected static final int STATE_UNLOADED = 0;
-    protected static final int STATE_LOADED = 1;
-    protected static final int STATE_ERROR = -1;
-
-    // Log a warning if a texture is larger along a dimension
-    private static final int MAX_TEXTURE_SIZE = 4096;
-
-    protected int mId = -1;
-    protected int mState;
-
-    protected int mWidth = UNSPECIFIED;
-    protected int mHeight = UNSPECIFIED;
-
-    protected int mTextureWidth;
-    protected int mTextureHeight;
-
-    private boolean mHasBorder;
-
-    protected GLCanvas mCanvasRef = null;
-    private static WeakHashMap<BasicTexture, Object> sAllTextures
-            = new WeakHashMap<BasicTexture, Object>();
-    private static ThreadLocal sInFinalizer = new ThreadLocal();
-
-    protected BasicTexture(GLCanvas canvas, int id, int state) {
-        setAssociatedCanvas(canvas);
-        mId = id;
-        mState = state;
-        synchronized (sAllTextures) {
-            sAllTextures.put(this, null);
-        }
-    }
-
-    protected BasicTexture() {
-        this(null, 0, STATE_UNLOADED);
-    }
-
-    protected void setAssociatedCanvas(GLCanvas canvas) {
-        mCanvasRef = canvas;
-    }
-
-    /**
-     * Sets the content size of this texture. In OpenGL, the actual texture
-     * size must be of power of 2, the size of the content may be smaller.
-     */
-    public void setSize(int width, int height) {
-        mWidth = width;
-        mHeight = height;
-        mTextureWidth = width > 0 ? Utils.nextPowerOf2(width) : 0;
-        mTextureHeight = height > 0 ? Utils.nextPowerOf2(height) : 0;
-        if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) {
-            Log.w(TAG, String.format("texture is too large: %d x %d",
-                    mTextureWidth, mTextureHeight), new Exception());
-        }
-    }
-
-    public boolean isFlippedVertically() {
-      return false;
-    }
-
-    public int getId() {
-        return mId;
-    }
-
-    @Override
-    public int getWidth() {
-        return mWidth;
-    }
-
-    @Override
-    public int getHeight() {
-        return mHeight;
-    }
-
-    // Returns the width rounded to the next power of 2.
-    public int getTextureWidth() {
-        return mTextureWidth;
-    }
-
-    // Returns the height rounded to the next power of 2.
-    public int getTextureHeight() {
-        return mTextureHeight;
-    }
-
-    // Returns true if the texture has one pixel transparent border around the
-    // actual content. This is used to avoid jigged edges.
-    //
-    // The jigged edges appear because we use GL_CLAMP_TO_EDGE for texture wrap
-    // mode (GL_CLAMP is not available in OpenGL ES), so a pixel partially
-    // covered by the texture will use the color of the edge texel. If we add
-    // the transparent border, the color of the edge texel will be mixed with
-    // appropriate amount of transparent.
-    //
-    // Currently our background is black, so we can draw the thumbnails without
-    // enabling blending.
-    public boolean hasBorder() {
-        return mHasBorder;
-    }
-
-    protected void setBorder(boolean hasBorder) {
-        mHasBorder = hasBorder;
-    }
-
-    @Override
-    public void draw(GLCanvas canvas, int x, int y) {
-        canvas.drawTexture(this, x, y, getWidth(), getHeight());
-    }
-
-    @Override
-    public void draw(GLCanvas canvas, int x, int y, int w, int h) {
-        canvas.drawTexture(this, x, y, w, h);
-    }
-
-    // onBind is called before GLCanvas binds this texture.
-    // It should make sure the data is uploaded to GL memory.
-    abstract protected boolean onBind(GLCanvas canvas);
-
-    // Returns the GL texture target for this texture (e.g. GL_TEXTURE_2D).
-    abstract protected int getTarget();
-
-    public boolean isLoaded() {
-        return mState == STATE_LOADED;
-    }
-
-    // recycle() is called when the texture will never be used again,
-    // so it can free all resources.
-    public void recycle() {
-        freeResource();
-    }
-
-    // yield() is called when the texture will not be used temporarily,
-    // so it can free some resources.
-    // The default implementation unloads the texture from GL memory, so
-    // the subclass should make sure it can reload the texture to GL memory
-    // later, or it will have to override this method.
-    public void yield() {
-        freeResource();
-    }
-
-    private void freeResource() {
-        GLCanvas canvas = mCanvasRef;
-        if (canvas != null && mId != -1) {
-            canvas.unloadTexture(this);
-            mId = -1; // Don't free it again.
-        }
-        mState = STATE_UNLOADED;
-        setAssociatedCanvas(null);
-    }
-
-    @Override
-    protected void finalize() {
-        sInFinalizer.set(BasicTexture.class);
-        recycle();
-        sInFinalizer.set(null);
-    }
-
-    // This is for deciding if we can call Bitmap's recycle().
-    // We cannot call Bitmap's recycle() in finalizer because at that point
-    // the finalizer of Bitmap may already be called so recycle() will crash.
-    public static boolean inFinalizer() {
-        return sInFinalizer.get() != null;
-    }
-
-    public static void yieldAllTextures() {
-        synchronized (sAllTextures) {
-            for (BasicTexture t : sAllTextures.keySet()) {
-                t.yield();
-            }
-        }
-    }
-
-    public static void invalidateAllTextures() {
-        synchronized (sAllTextures) {
-            for (BasicTexture t : sAllTextures.keySet()) {
-                t.mState = STATE_UNLOADED;
-                t.setAssociatedCanvas(null);
-            }
-        }
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/BitmapTexture.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/BitmapTexture.java
deleted file mode 100644
index f8b01cb..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/BitmapTexture.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.glrenderer;
-
-import android.graphics.Bitmap;
-
-import com.android.gallery3d.common.Utils;
-
-// BitmapTexture is a texture whose content is specified by a fixed Bitmap.
-//
-// The texture does not own the Bitmap. The user should make sure the Bitmap
-// is valid during the texture's lifetime. When the texture is recycled, it
-// does not free the Bitmap.
-public class BitmapTexture extends UploadedTexture {
-    protected Bitmap mContentBitmap;
-
-    public BitmapTexture(Bitmap bitmap) {
-        this(bitmap, false);
-    }
-
-    public BitmapTexture(Bitmap bitmap, boolean hasBorder) {
-        super(hasBorder);
-        Utils.assertTrue(bitmap != null && !bitmap.isRecycled());
-        mContentBitmap = bitmap;
-    }
-
-    @Override
-    protected void onFreeBitmap(Bitmap bitmap) {
-        // Do nothing.
-    }
-
-    @Override
-    protected Bitmap onGetBitmap() {
-        return mContentBitmap;
-    }
-
-    public Bitmap getBitmap() {
-        return mContentBitmap;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLCanvas.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLCanvas.java
deleted file mode 100644
index 5b07477..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLCanvas.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.glrenderer;
-
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.graphics.RectF;
-
-//
-// GLCanvas gives a convenient interface to draw using OpenGL.
-//
-// When a rectangle is specified in this interface, it means the region
-// [x, x+width) * [y, y+height)
-//
-public interface GLCanvas {
-
-    public GLId getGLId();
-
-    // Tells GLCanvas the size of the underlying GL surface. This should be
-    // called before first drawing and when the size of GL surface is changed.
-    // This is called by GLRoot and should not be called by the clients
-    // who only want to draw on the GLCanvas. Both width and height must be
-    // nonnegative.
-    public abstract void setSize(int width, int height);
-
-    // Clear the drawing buffers. This should only be used by GLRoot.
-    public abstract void clearBuffer();
-
-    public abstract void clearBuffer(float[] argb);
-
-    // Sets and gets the current alpha, alpha must be in [0, 1].
-    public abstract void setAlpha(float alpha);
-
-    public abstract float getAlpha();
-
-    // (current alpha) = (current alpha) * alpha
-    public abstract void multiplyAlpha(float alpha);
-
-    // Change the current transform matrix.
-    public abstract void translate(float x, float y, float z);
-
-    public abstract void translate(float x, float y);
-
-    public abstract void scale(float sx, float sy, float sz);
-
-    public abstract void rotate(float angle, float x, float y, float z);
-
-    public abstract void multiplyMatrix(float[] mMatrix, int offset);
-
-    // Pushes the configuration state (matrix, and alpha) onto
-    // a private stack.
-    public abstract void save();
-
-    // Same as save(), but only save those specified in saveFlags.
-    public abstract void save(int saveFlags);
-
-    public static final int SAVE_FLAG_ALL = 0xFFFFFFFF;
-    public static final int SAVE_FLAG_ALPHA = 0x01;
-    public static final int SAVE_FLAG_MATRIX = 0x02;
-
-    // Pops from the top of the stack as current configuration state (matrix,
-    // alpha, and clip). This call balances a previous call to save(), and is
-    // used to remove all modifications to the configuration state since the
-    // last save call.
-    public abstract void restore();
-
-    // Draws a line using the specified paint from (x1, y1) to (x2, y2).
-    // (Both end points are included).
-    public abstract void drawLine(float x1, float y1, float x2, float y2, GLPaint paint);
-
-    // Draws a rectangle using the specified paint from (x1, y1) to (x2, y2).
-    // (Both end points are included).
-    public abstract void drawRect(float x1, float y1, float x2, float y2, GLPaint paint);
-
-    // Fills the specified rectangle with the specified color.
-    public abstract void fillRect(float x, float y, float width, float height, int color);
-
-    // Draws a texture to the specified rectangle.
-    public abstract void drawTexture(
-            BasicTexture texture, int x, int y, int width, int height);
-
-    public abstract void drawMesh(BasicTexture tex, int x, int y, int xyBuffer,
-            int uvBuffer, int indexBuffer, int indexCount);
-
-    // Draws the source rectangle part of the texture to the target rectangle.
-    public abstract void drawTexture(BasicTexture texture, RectF source, RectF target);
-
-    // Draw a texture with a specified texture transform.
-    public abstract void drawTexture(BasicTexture texture, float[] mTextureTransform,
-                int x, int y, int w, int h);
-
-    // Draw two textures to the specified rectangle. The actual texture used is
-    // from * (1 - ratio) + to * ratio
-    // The two textures must have the same size.
-    public abstract void drawMixed(BasicTexture from, int toColor,
-            float ratio, int x, int y, int w, int h);
-
-    // Draw a region of a texture and a specified color to the specified
-    // rectangle. The actual color used is from * (1 - ratio) + to * ratio.
-    // The region of the texture is defined by parameter "src". The target
-    // rectangle is specified by parameter "target".
-    public abstract void drawMixed(BasicTexture from, int toColor,
-            float ratio, RectF src, RectF target);
-
-    // Unloads the specified texture from the canvas. The resource allocated
-    // to draw the texture will be released. The specified texture will return
-    // to the unloaded state. This function should be called only from
-    // BasicTexture or its descendant
-    public abstract boolean unloadTexture(BasicTexture texture);
-
-    // Delete the specified buffer object, similar to unloadTexture.
-    public abstract void deleteBuffer(int bufferId);
-
-    // Delete the textures and buffers in GL side. This function should only be
-    // called in the GL thread.
-    public abstract void deleteRecycledResources();
-
-    // Dump statistics information and clear the counters. For debug only.
-    public abstract void dumpStatisticsAndClear();
-
-    public abstract void beginRenderTarget(RawTexture texture);
-
-    public abstract void endRenderTarget();
-
-    /**
-     * Sets texture parameters to use GL_CLAMP_TO_EDGE for both
-     * GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T. Sets texture parameters to be
-     * GL_LINEAR for GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAG_FILTER.
-     * bindTexture() must be called prior to this.
-     *
-     * @param texture The texture to set parameters on.
-     */
-    public abstract void setTextureParameters(BasicTexture texture);
-
-    /**
-     * Initializes the texture to a size by calling texImage2D on it.
-     *
-     * @param texture The texture to initialize the size.
-     * @param format The texture format (e.g. GL_RGBA)
-     * @param type The texture type (e.g. GL_UNSIGNED_BYTE)
-     */
-    public abstract void initializeTextureSize(BasicTexture texture, int format, int type);
-
-    /**
-     * Initializes the texture to a size by calling texImage2D on it.
-     *
-     * @param texture The texture to initialize the size.
-     * @param bitmap The bitmap to initialize the bitmap with.
-     */
-    public abstract void initializeTexture(BasicTexture texture, Bitmap bitmap);
-
-    /**
-     * Calls glTexSubImage2D to upload a bitmap to the texture.
-     *
-     * @param texture The target texture to write to.
-     * @param xOffset Specifies a texel offset in the x direction within the
-     *            texture array.
-     * @param yOffset Specifies a texel offset in the y direction within the
-     *            texture array.
-     * @param format The texture format (e.g. GL_RGBA)
-     * @param type The texture type (e.g. GL_UNSIGNED_BYTE)
-     */
-    public abstract void texSubImage2D(BasicTexture texture, int xOffset, int yOffset,
-            Bitmap bitmap,
-            int format, int type);
-
-    /**
-     * Generates buffers and uploads the buffer data.
-     *
-     * @param buffer The buffer to upload
-     * @return The buffer ID that was generated.
-     */
-    public abstract int uploadBuffer(java.nio.FloatBuffer buffer);
-
-    /**
-     * Generates buffers and uploads the element array buffer data.
-     *
-     * @param buffer The buffer to upload
-     * @return The buffer ID that was generated.
-     */
-    public abstract int uploadBuffer(java.nio.ByteBuffer buffer);
-
-    /**
-     * After LightCycle makes GL calls, this method is called to restore the GL
-     * configuration to the one expected by GLCanvas.
-     */
-    public abstract void recoverFromLightCycle();
-
-    /**
-     * Gets the bounds given by x, y, width, and height as well as the internal
-     * matrix state. There is no special handling for non-90-degree rotations.
-     * It only considers the lower-left and upper-right corners as the bounds.
-     *
-     * @param bounds The output bounds to write to.
-     * @param x The left side of the input rectangle.
-     * @param y The bottom of the input rectangle.
-     * @param width The width of the input rectangle.
-     * @param height The height of the input rectangle.
-     */
-    public abstract void getBounds(Rect bounds, int x, int y, int width, int height);
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java
deleted file mode 100644
index 933260b..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20Canvas.java
+++ /dev/null
@@ -1,1008 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.gallery3d.glrenderer;
-
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.opengl.GLES20;
-import android.opengl.GLUtils;
-import android.opengl.Matrix;
-import android.util.Log;
-
-import java.nio.Buffer;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-
-public class GLES20Canvas implements GLCanvas {
-    // ************** Constants **********************
-    private static final String TAG = GLES20Canvas.class.getSimpleName();
-    private static final int FLOAT_SIZE = Float.SIZE / Byte.SIZE;
-    private static final float OPAQUE_ALPHA = 0.95f;
-
-    private static final int COORDS_PER_VERTEX = 2;
-    private static final int VERTEX_STRIDE = COORDS_PER_VERTEX * FLOAT_SIZE;
-
-    private static final int COUNT_FILL_VERTEX = 4;
-    private static final int COUNT_LINE_VERTEX = 2;
-    private static final int COUNT_RECT_VERTEX = 4;
-    private static final int OFFSET_FILL_RECT = 0;
-    private static final int OFFSET_DRAW_LINE = OFFSET_FILL_RECT + COUNT_FILL_VERTEX;
-    private static final int OFFSET_DRAW_RECT = OFFSET_DRAW_LINE + COUNT_LINE_VERTEX;
-
-    private static final float[] BOX_COORDINATES = {
-            0, 0, // Fill rectangle
-            1, 0,
-            0, 1,
-            1, 1,
-            0, 0, // Draw line
-            1, 1,
-            0, 0, // Draw rectangle outline
-            0, 1,
-            1, 1,
-            1, 0,
-    };
-
-    private static final float[] BOUNDS_COORDINATES = {
-        0, 0, 0, 1,
-        1, 1, 0, 1,
-    };
-
-    private static final String POSITION_ATTRIBUTE = "aPosition";
-    private static final String COLOR_UNIFORM = "uColor";
-    private static final String MATRIX_UNIFORM = "uMatrix";
-    private static final String TEXTURE_MATRIX_UNIFORM = "uTextureMatrix";
-    private static final String TEXTURE_SAMPLER_UNIFORM = "uTextureSampler";
-    private static final String ALPHA_UNIFORM = "uAlpha";
-    private static final String TEXTURE_COORD_ATTRIBUTE = "aTextureCoordinate";
-
-    private static final String DRAW_VERTEX_SHADER = ""
-            + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
-            + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
-            + "void main() {\n"
-            + "  vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
-            + "  gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
-            + "}\n";
-
-    private static final String DRAW_FRAGMENT_SHADER = ""
-            + "precision mediump float;\n"
-            + "uniform vec4 " + COLOR_UNIFORM + ";\n"
-            + "void main() {\n"
-            + "  gl_FragColor = " + COLOR_UNIFORM + ";\n"
-            + "}\n";
-
-    private static final String TEXTURE_VERTEX_SHADER = ""
-            + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
-            + "uniform mat4 " + TEXTURE_MATRIX_UNIFORM + ";\n"
-            + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
-            + "varying vec2 vTextureCoord;\n"
-            + "void main() {\n"
-            + "  vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
-            + "  gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
-            + "  vTextureCoord = (" + TEXTURE_MATRIX_UNIFORM + " * pos).xy;\n"
-            + "}\n";
-
-    private static final String MESH_VERTEX_SHADER = ""
-            + "uniform mat4 " + MATRIX_UNIFORM + ";\n"
-            + "attribute vec2 " + POSITION_ATTRIBUTE + ";\n"
-            + "attribute vec2 " + TEXTURE_COORD_ATTRIBUTE + ";\n"
-            + "varying vec2 vTextureCoord;\n"
-            + "void main() {\n"
-            + "  vec4 pos = vec4(" + POSITION_ATTRIBUTE + ", 0.0, 1.0);\n"
-            + "  gl_Position = " + MATRIX_UNIFORM + " * pos;\n"
-            + "  vTextureCoord = " + TEXTURE_COORD_ATTRIBUTE + ";\n"
-            + "}\n";
-
-    private static final String TEXTURE_FRAGMENT_SHADER = ""
-            + "precision mediump float;\n"
-            + "varying vec2 vTextureCoord;\n"
-            + "uniform float " + ALPHA_UNIFORM + ";\n"
-            + "uniform sampler2D " + TEXTURE_SAMPLER_UNIFORM + ";\n"
-            + "void main() {\n"
-            + "  gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
-            + "  gl_FragColor *= " + ALPHA_UNIFORM + ";\n"
-            + "}\n";
-
-    private static final String OES_TEXTURE_FRAGMENT_SHADER = ""
-            + "#extension GL_OES_EGL_image_external : require\n"
-            + "precision mediump float;\n"
-            + "varying vec2 vTextureCoord;\n"
-            + "uniform float " + ALPHA_UNIFORM + ";\n"
-            + "uniform samplerExternalOES " + TEXTURE_SAMPLER_UNIFORM + ";\n"
-            + "void main() {\n"
-            + "  gl_FragColor = texture2D(" + TEXTURE_SAMPLER_UNIFORM + ", vTextureCoord);\n"
-            + "  gl_FragColor *= " + ALPHA_UNIFORM + ";\n"
-            + "}\n";
-
-    private static final int INITIAL_RESTORE_STATE_SIZE = 8;
-    private static final int MATRIX_SIZE = 16;
-
-    // Keep track of restore state
-    private float[] mMatrices = new float[INITIAL_RESTORE_STATE_SIZE * MATRIX_SIZE];
-    private float[] mAlphas = new float[INITIAL_RESTORE_STATE_SIZE];
-    private IntArray mSaveFlags = new IntArray();
-
-    private int mCurrentAlphaIndex = 0;
-    private int mCurrentMatrixIndex = 0;
-
-    // Viewport size
-    private int mWidth;
-    private int mHeight;
-
-    // Projection matrix
-    private float[] mProjectionMatrix = new float[MATRIX_SIZE];
-
-    // Screen size for when we aren't bound to a texture
-    private int mScreenWidth;
-    private int mScreenHeight;
-
-    // GL programs
-    private int mDrawProgram;
-    private int mTextureProgram;
-    private int mOesTextureProgram;
-    private int mMeshProgram;
-
-    // GL buffer containing BOX_COORDINATES
-    private int mBoxCoordinates;
-
-    // Handle indices -- common
-    private static final int INDEX_POSITION = 0;
-    private static final int INDEX_MATRIX = 1;
-
-    // Handle indices -- draw
-    private static final int INDEX_COLOR = 2;
-
-    // Handle indices -- texture
-    private static final int INDEX_TEXTURE_MATRIX = 2;
-    private static final int INDEX_TEXTURE_SAMPLER = 3;
-    private static final int INDEX_ALPHA = 4;
-
-    // Handle indices -- mesh
-    private static final int INDEX_TEXTURE_COORD = 2;
-
-    private abstract static class ShaderParameter {
-        public int handle;
-        protected final String mName;
-
-        public ShaderParameter(String name) {
-            mName = name;
-        }
-
-        public abstract void loadHandle(int program);
-    }
-
-    private static class UniformShaderParameter extends ShaderParameter {
-        public UniformShaderParameter(String name) {
-            super(name);
-        }
-
-        @Override
-        public void loadHandle(int program) {
-            handle = GLES20.glGetUniformLocation(program, mName);
-            checkError();
-        }
-    }
-
-    private static class AttributeShaderParameter extends ShaderParameter {
-        public AttributeShaderParameter(String name) {
-            super(name);
-        }
-
-        @Override
-        public void loadHandle(int program) {
-            handle = GLES20.glGetAttribLocation(program, mName);
-            checkError();
-        }
-    }
-
-    ShaderParameter[] mDrawParameters = {
-            new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
-            new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
-            new UniformShaderParameter(COLOR_UNIFORM), // INDEX_COLOR
-    };
-    ShaderParameter[] mTextureParameters = {
-            new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
-            new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
-            new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
-            new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
-            new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
-    };
-    ShaderParameter[] mOesTextureParameters = {
-            new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
-            new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
-            new UniformShaderParameter(TEXTURE_MATRIX_UNIFORM), // INDEX_TEXTURE_MATRIX
-            new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
-            new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
-    };
-    ShaderParameter[] mMeshParameters = {
-            new AttributeShaderParameter(POSITION_ATTRIBUTE), // INDEX_POSITION
-            new UniformShaderParameter(MATRIX_UNIFORM), // INDEX_MATRIX
-            new AttributeShaderParameter(TEXTURE_COORD_ATTRIBUTE), // INDEX_TEXTURE_COORD
-            new UniformShaderParameter(TEXTURE_SAMPLER_UNIFORM), // INDEX_TEXTURE_SAMPLER
-            new UniformShaderParameter(ALPHA_UNIFORM), // INDEX_ALPHA
-    };
-
-    private final IntArray mUnboundTextures = new IntArray();
-    private final IntArray mDeleteBuffers = new IntArray();
-
-    // Keep track of statistics for debugging
-    private int mCountDrawMesh = 0;
-    private int mCountTextureRect = 0;
-    private int mCountFillRect = 0;
-    private int mCountDrawLine = 0;
-
-    // Buffer for framebuffer IDs -- we keep track so we can switch the attached
-    // texture.
-    private int[] mFrameBuffer = new int[1];
-
-    // Bound textures.
-    private ArrayList<RawTexture> mTargetTextures = new ArrayList<RawTexture>();
-
-    // Temporary variables used within calculations
-    private final float[] mTempMatrix = new float[32];
-    private final float[] mTempColor = new float[4];
-    private final RectF mTempSourceRect = new RectF();
-    private final RectF mTempTargetRect = new RectF();
-    private final float[] mTempTextureMatrix = new float[MATRIX_SIZE];
-    private final int[] mTempIntArray = new int[1];
-
-    private static final GLId mGLId = new GLES20IdImpl();
-
-    public GLES20Canvas() {
-        Matrix.setIdentityM(mTempTextureMatrix, 0);
-        Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
-        mAlphas[mCurrentAlphaIndex] = 1f;
-        mTargetTextures.add(null);
-
-        FloatBuffer boxBuffer = createBuffer(BOX_COORDINATES);
-        mBoxCoordinates = uploadBuffer(boxBuffer);
-
-        int drawVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, DRAW_VERTEX_SHADER);
-        int textureVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, TEXTURE_VERTEX_SHADER);
-        int meshVertexShader = loadShader(GLES20.GL_VERTEX_SHADER, MESH_VERTEX_SHADER);
-        int drawFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, DRAW_FRAGMENT_SHADER);
-        int textureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, TEXTURE_FRAGMENT_SHADER);
-        int oesTextureFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
-                OES_TEXTURE_FRAGMENT_SHADER);
-
-        mDrawProgram = assembleProgram(drawVertexShader, drawFragmentShader, mDrawParameters);
-        mTextureProgram = assembleProgram(textureVertexShader, textureFragmentShader,
-                mTextureParameters);
-        mOesTextureProgram = assembleProgram(textureVertexShader, oesTextureFragmentShader,
-                mOesTextureParameters);
-        mMeshProgram = assembleProgram(meshVertexShader, textureFragmentShader, mMeshParameters);
-        GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
-        checkError();
-    }
-
-    private static FloatBuffer createBuffer(float[] values) {
-        // First create an nio buffer, then create a VBO from it.
-        int size = values.length * FLOAT_SIZE;
-        FloatBuffer buffer = ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder())
-                .asFloatBuffer();
-        buffer.put(values, 0, values.length).position(0);
-        return buffer;
-    }
-
-    private int assembleProgram(int vertexShader, int fragmentShader, ShaderParameter[] params) {
-        int program = GLES20.glCreateProgram();
-        checkError();
-        if (program == 0) {
-            throw new RuntimeException("Cannot create GL program: " + GLES20.glGetError());
-        }
-        GLES20.glAttachShader(program, vertexShader);
-        checkError();
-        GLES20.glAttachShader(program, fragmentShader);
-        checkError();
-        GLES20.glLinkProgram(program);
-        checkError();
-        int[] mLinkStatus = mTempIntArray;
-        GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, mLinkStatus, 0);
-        if (mLinkStatus[0] != GLES20.GL_TRUE) {
-            Log.e(TAG, "Could not link program: ");
-            Log.e(TAG, GLES20.glGetProgramInfoLog(program));
-            GLES20.glDeleteProgram(program);
-            program = 0;
-        }
-        for (int i = 0; i < params.length; i++) {
-            params[i].loadHandle(program);
-        }
-        return program;
-    }
-
-    private static int loadShader(int type, String shaderCode) {
-        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
-        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
-        int shader = GLES20.glCreateShader(type);
-
-        // add the source code to the shader and compile it
-        GLES20.glShaderSource(shader, shaderCode);
-        checkError();
-        GLES20.glCompileShader(shader);
-        checkError();
-
-        return shader;
-    }
-
-    @Override
-    public void setSize(int width, int height) {
-        mWidth = width;
-        mHeight = height;
-        GLES20.glViewport(0, 0, mWidth, mHeight);
-        checkError();
-        Matrix.setIdentityM(mMatrices, mCurrentMatrixIndex);
-        Matrix.orthoM(mProjectionMatrix, 0, 0, width, 0, height, -1, 1);
-        if (getTargetTexture() == null) {
-            mScreenWidth = width;
-            mScreenHeight = height;
-            Matrix.translateM(mMatrices, mCurrentMatrixIndex, 0, height, 0);
-            Matrix.scaleM(mMatrices, mCurrentMatrixIndex, 1, -1, 1);
-        }
-    }
-
-    @Override
-    public void clearBuffer() {
-        GLES20.glClearColor(0f, 0f, 0f, 1f);
-        checkError();
-        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
-        checkError();
-    }
-
-    @Override
-    public void clearBuffer(float[] argb) {
-        GLES20.glClearColor(argb[1], argb[2], argb[3], argb[0]);
-        checkError();
-        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
-        checkError();
-    }
-
-    @Override
-    public float getAlpha() {
-        return mAlphas[mCurrentAlphaIndex];
-    }
-
-    @Override
-    public void setAlpha(float alpha) {
-        mAlphas[mCurrentAlphaIndex] = alpha;
-    }
-
-    @Override
-    public void multiplyAlpha(float alpha) {
-        setAlpha(getAlpha() * alpha);
-    }
-
-    @Override
-    public void translate(float x, float y, float z) {
-        Matrix.translateM(mMatrices, mCurrentMatrixIndex, x, y, z);
-    }
-
-    // This is a faster version of translate(x, y, z) because
-    // (1) we knows z = 0, (2) we inline the Matrix.translateM call,
-    // (3) we unroll the loop
-    @Override
-    public void translate(float x, float y) {
-        int index = mCurrentMatrixIndex;
-        float[] m = mMatrices;
-        m[index + 12] += m[index + 0] * x + m[index + 4] * y;
-        m[index + 13] += m[index + 1] * x + m[index + 5] * y;
-        m[index + 14] += m[index + 2] * x + m[index + 6] * y;
-        m[index + 15] += m[index + 3] * x + m[index + 7] * y;
-    }
-
-    @Override
-    public void scale(float sx, float sy, float sz) {
-        Matrix.scaleM(mMatrices, mCurrentMatrixIndex, sx, sy, sz);
-    }
-
-    @Override
-    public void rotate(float angle, float x, float y, float z) {
-        if (angle == 0f) {
-            return;
-        }
-        float[] temp = mTempMatrix;
-        Matrix.setRotateM(temp, 0, angle, x, y, z);
-        float[] matrix = mMatrices;
-        int index = mCurrentMatrixIndex;
-        Matrix.multiplyMM(temp, MATRIX_SIZE, matrix, index, temp, 0);
-        System.arraycopy(temp, MATRIX_SIZE, matrix, index, MATRIX_SIZE);
-    }
-
-    @Override
-    public void multiplyMatrix(float[] matrix, int offset) {
-        float[] temp = mTempMatrix;
-        float[] currentMatrix = mMatrices;
-        int index = mCurrentMatrixIndex;
-        Matrix.multiplyMM(temp, 0, currentMatrix, index, matrix, offset);
-        System.arraycopy(temp, 0, currentMatrix, index, 16);
-    }
-
-    @Override
-    public void save() {
-        save(SAVE_FLAG_ALL);
-    }
-
-    @Override
-    public void save(int saveFlags) {
-        boolean saveAlpha = (saveFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
-        if (saveAlpha) {
-            float currentAlpha = getAlpha();
-            mCurrentAlphaIndex++;
-            if (mAlphas.length <= mCurrentAlphaIndex) {
-                mAlphas = Arrays.copyOf(mAlphas, mAlphas.length * 2);
-            }
-            mAlphas[mCurrentAlphaIndex] = currentAlpha;
-        }
-        boolean saveMatrix = (saveFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
-        if (saveMatrix) {
-            int currentIndex = mCurrentMatrixIndex;
-            mCurrentMatrixIndex += MATRIX_SIZE;
-            if (mMatrices.length <= mCurrentMatrixIndex) {
-                mMatrices = Arrays.copyOf(mMatrices, mMatrices.length * 2);
-            }
-            System.arraycopy(mMatrices, currentIndex, mMatrices, mCurrentMatrixIndex, MATRIX_SIZE);
-        }
-        mSaveFlags.add(saveFlags);
-    }
-
-    @Override
-    public void restore() {
-        int restoreFlags = mSaveFlags.removeLast();
-        boolean restoreAlpha = (restoreFlags & SAVE_FLAG_ALPHA) == SAVE_FLAG_ALPHA;
-        if (restoreAlpha) {
-            mCurrentAlphaIndex--;
-        }
-        boolean restoreMatrix = (restoreFlags & SAVE_FLAG_MATRIX) == SAVE_FLAG_MATRIX;
-        if (restoreMatrix) {
-            mCurrentMatrixIndex -= MATRIX_SIZE;
-        }
-    }
-
-    @Override
-    public void drawLine(float x1, float y1, float x2, float y2, GLPaint paint) {
-        draw(GLES20.GL_LINE_STRIP, OFFSET_DRAW_LINE, COUNT_LINE_VERTEX, x1, y1, x2 - x1, y2 - y1,
-                paint);
-        mCountDrawLine++;
-    }
-
-    @Override
-    public void drawRect(float x, float y, float width, float height, GLPaint paint) {
-        draw(GLES20.GL_LINE_LOOP, OFFSET_DRAW_RECT, COUNT_RECT_VERTEX, x, y, width, height, paint);
-        mCountDrawLine++;
-    }
-
-    private void draw(int type, int offset, int count, float x, float y, float width, float height,
-            GLPaint paint) {
-        draw(type, offset, count, x, y, width, height, paint.getColor(), paint.getLineWidth());
-    }
-
-    private void draw(int type, int offset, int count, float x, float y, float width, float height,
-            int color, float lineWidth) {
-        prepareDraw(offset, color, lineWidth);
-        draw(mDrawParameters, type, count, x, y, width, height);
-    }
-
-    private void prepareDraw(int offset, int color, float lineWidth) {
-        GLES20.glUseProgram(mDrawProgram);
-        checkError();
-        if (lineWidth > 0) {
-            GLES20.glLineWidth(lineWidth);
-            checkError();
-        }
-        float[] colorArray = getColor(color);
-        boolean blendingEnabled = (colorArray[3] < 1f);
-        enableBlending(blendingEnabled);
-        if (blendingEnabled) {
-            GLES20.glBlendColor(colorArray[0], colorArray[1], colorArray[2], colorArray[3]);
-            checkError();
-        }
-
-        GLES20.glUniform4fv(mDrawParameters[INDEX_COLOR].handle, 1, colorArray, 0);
-        setPosition(mDrawParameters, offset);
-        checkError();
-    }
-
-    private float[] getColor(int color) {
-        float alpha = ((color >>> 24) & 0xFF) / 255f * getAlpha();
-        float red = ((color >>> 16) & 0xFF) / 255f * alpha;
-        float green = ((color >>> 8) & 0xFF) / 255f * alpha;
-        float blue = (color & 0xFF) / 255f * alpha;
-        mTempColor[0] = red;
-        mTempColor[1] = green;
-        mTempColor[2] = blue;
-        mTempColor[3] = alpha;
-        return mTempColor;
-    }
-
-    private void enableBlending(boolean enableBlending) {
-        if (enableBlending) {
-            GLES20.glEnable(GLES20.GL_BLEND);
-            checkError();
-        } else {
-            GLES20.glDisable(GLES20.GL_BLEND);
-            checkError();
-        }
-    }
-
-    private void setPosition(ShaderParameter[] params, int offset) {
-        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mBoxCoordinates);
-        checkError();
-        GLES20.glVertexAttribPointer(params[INDEX_POSITION].handle, COORDS_PER_VERTEX,
-                GLES20.GL_FLOAT, false, VERTEX_STRIDE, offset * VERTEX_STRIDE);
-        checkError();
-        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
-        checkError();
-    }
-
-    private void draw(ShaderParameter[] params, int type, int count, float x, float y, float width,
-            float height) {
-        setMatrix(params, x, y, width, height);
-        int positionHandle = params[INDEX_POSITION].handle;
-        GLES20.glEnableVertexAttribArray(positionHandle);
-        checkError();
-        GLES20.glDrawArrays(type, 0, count);
-        checkError();
-        GLES20.glDisableVertexAttribArray(positionHandle);
-        checkError();
-    }
-
-    private void setMatrix(ShaderParameter[] params, float x, float y, float width, float height) {
-        Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f);
-        Matrix.scaleM(mTempMatrix, 0, width, height, 1f);
-        Matrix.multiplyMM(mTempMatrix, MATRIX_SIZE, mProjectionMatrix, 0, mTempMatrix, 0);
-        GLES20.glUniformMatrix4fv(params[INDEX_MATRIX].handle, 1, false, mTempMatrix, MATRIX_SIZE);
-        checkError();
-    }
-
-    @Override
-    public void fillRect(float x, float y, float width, float height, int color) {
-        draw(GLES20.GL_TRIANGLE_STRIP, OFFSET_FILL_RECT, COUNT_FILL_VERTEX, x, y, width, height,
-                color, 0f);
-        mCountFillRect++;
-    }
-
-    @Override
-    public void drawTexture(BasicTexture texture, int x, int y, int width, int height) {
-        if (width <= 0 || height <= 0) {
-            return;
-        }
-        copyTextureCoordinates(texture, mTempSourceRect);
-        mTempTargetRect.set(x, y, x + width, y + height);
-        convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
-        drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
-    }
-
-    private static void copyTextureCoordinates(BasicTexture texture, RectF outRect) {
-        int left = 0;
-        int top = 0;
-        int right = texture.getWidth();
-        int bottom = texture.getHeight();
-        if (texture.hasBorder()) {
-            left = 1;
-            top = 1;
-            right -= 1;
-            bottom -= 1;
-        }
-        outRect.set(left, top, right, bottom);
-    }
-
-    @Override
-    public void drawTexture(BasicTexture texture, RectF source, RectF target) {
-        if (target.width() <= 0 || target.height() <= 0) {
-            return;
-        }
-        mTempSourceRect.set(source);
-        mTempTargetRect.set(target);
-
-        convertCoordinate(mTempSourceRect, mTempTargetRect, texture);
-        drawTextureRect(texture, mTempSourceRect, mTempTargetRect);
-    }
-
-    @Override
-    public void drawTexture(BasicTexture texture, float[] textureTransform, int x, int y, int w,
-            int h) {
-        if (w <= 0 || h <= 0) {
-            return;
-        }
-        mTempTargetRect.set(x, y, x + w, y + h);
-        drawTextureRect(texture, textureTransform, mTempTargetRect);
-    }
-
-    private void drawTextureRect(BasicTexture texture, RectF source, RectF target) {
-        setTextureMatrix(source);
-        drawTextureRect(texture, mTempTextureMatrix, target);
-    }
-
-    private void setTextureMatrix(RectF source) {
-        mTempTextureMatrix[0] = source.width();
-        mTempTextureMatrix[5] = source.height();
-        mTempTextureMatrix[12] = source.left;
-        mTempTextureMatrix[13] = source.top;
-    }
-
-    // This function changes the source coordinate to the texture coordinates.
-    // It also clips the source and target coordinates if it is beyond the
-    // bound of the texture.
-    private static void convertCoordinate(RectF source, RectF target, BasicTexture texture) {
-        int width = texture.getWidth();
-        int height = texture.getHeight();
-        int texWidth = texture.getTextureWidth();
-        int texHeight = texture.getTextureHeight();
-        // Convert to texture coordinates
-        source.left /= texWidth;
-        source.right /= texWidth;
-        source.top /= texHeight;
-        source.bottom /= texHeight;
-
-        // Clip if the rendering range is beyond the bound of the texture.
-        float xBound = (float) width / texWidth;
-        if (source.right > xBound) {
-            target.right = target.left + target.width() * (xBound - source.left) / source.width();
-            source.right = xBound;
-        }
-        float yBound = (float) height / texHeight;
-        if (source.bottom > yBound) {
-            target.bottom = target.top + target.height() * (yBound - source.top) / source.height();
-            source.bottom = yBound;
-        }
-    }
-
-    private void drawTextureRect(BasicTexture texture, float[] textureMatrix, RectF target) {
-        ShaderParameter[] params = prepareTexture(texture);
-        setPosition(params, OFFSET_FILL_RECT);
-        GLES20.glUniformMatrix4fv(params[INDEX_TEXTURE_MATRIX].handle, 1, false, textureMatrix, 0);
-        checkError();
-        if (texture.isFlippedVertically()) {
-            save(SAVE_FLAG_MATRIX);
-            translate(0, target.centerY());
-            scale(1, -1, 1);
-            translate(0, -target.centerY());
-        }
-        draw(params, GLES20.GL_TRIANGLE_STRIP, COUNT_FILL_VERTEX, target.left, target.top,
-                target.width(), target.height());
-        if (texture.isFlippedVertically()) {
-            restore();
-        }
-        mCountTextureRect++;
-    }
-
-    private ShaderParameter[] prepareTexture(BasicTexture texture) {
-        ShaderParameter[] params;
-        int program;
-        if (texture.getTarget() == GLES20.GL_TEXTURE_2D) {
-            params = mTextureParameters;
-            program = mTextureProgram;
-        } else {
-            params = mOesTextureParameters;
-            program = mOesTextureProgram;
-        }
-        prepareTexture(texture, program, params);
-        return params;
-    }
-
-    private void prepareTexture(BasicTexture texture, int program, ShaderParameter[] params) {
-        deleteRecycledResources();
-        GLES20.glUseProgram(program);
-        checkError();
-        enableBlending(!texture.isOpaque() || getAlpha() < OPAQUE_ALPHA);
-        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
-        checkError();
-        texture.onBind(this);
-        GLES20.glBindTexture(texture.getTarget(), texture.getId());
-        checkError();
-        GLES20.glUniform1i(params[INDEX_TEXTURE_SAMPLER].handle, 0);
-        checkError();
-        GLES20.glUniform1f(params[INDEX_ALPHA].handle, getAlpha());
-        checkError();
-    }
-
-    @Override
-    public void drawMesh(BasicTexture texture, int x, int y, int xyBuffer, int uvBuffer,
-            int indexBuffer, int indexCount) {
-        prepareTexture(texture, mMeshProgram, mMeshParameters);
-
-        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
-        checkError();
-
-        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, xyBuffer);
-        checkError();
-        int positionHandle = mMeshParameters[INDEX_POSITION].handle;
-        GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false,
-                VERTEX_STRIDE, 0);
-        checkError();
-
-        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, uvBuffer);
-        checkError();
-        int texCoordHandle = mMeshParameters[INDEX_TEXTURE_COORD].handle;
-        GLES20.glVertexAttribPointer(texCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
-                false, VERTEX_STRIDE, 0);
-        checkError();
-        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
-        checkError();
-
-        GLES20.glEnableVertexAttribArray(positionHandle);
-        checkError();
-        GLES20.glEnableVertexAttribArray(texCoordHandle);
-        checkError();
-
-        setMatrix(mMeshParameters, x, y, 1, 1);
-        GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexCount, GLES20.GL_UNSIGNED_BYTE, 0);
-        checkError();
-
-        GLES20.glDisableVertexAttribArray(positionHandle);
-        checkError();
-        GLES20.glDisableVertexAttribArray(texCoordHandle);
-        checkError();
-        GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
-        checkError();
-        mCountDrawMesh++;
-    }
-
-    @Override
-    public void drawMixed(BasicTexture texture, int toColor, float ratio, int x, int y, int w, int h) {
-        copyTextureCoordinates(texture, mTempSourceRect);
-        mTempTargetRect.set(x, y, x + w, y + h);
-        drawMixed(texture, toColor, ratio, mTempSourceRect, mTempTargetRect);
-    }
-
-    @Override
-    public void drawMixed(BasicTexture texture, int toColor, float ratio, RectF source, RectF target) {
-        if (target.width() <= 0 || target.height() <= 0) {
-            return;
-        }
-        save(SAVE_FLAG_ALPHA);
-
-        float currentAlpha = getAlpha();
-        float cappedRatio = Math.min(1f, Math.max(0f, ratio));
-
-        float textureAlpha = (1f - cappedRatio) * currentAlpha;
-        setAlpha(textureAlpha);
-        drawTexture(texture, source, target);
-
-        float colorAlpha = cappedRatio * currentAlpha;
-        setAlpha(colorAlpha);
-        fillRect(target.left, target.top, target.width(), target.height(), toColor);
-
-        restore();
-    }
-
-    @Override
-    public boolean unloadTexture(BasicTexture texture) {
-        boolean unload = texture.isLoaded();
-        if (unload) {
-            synchronized (mUnboundTextures) {
-                mUnboundTextures.add(texture.getId());
-            }
-        }
-        return unload;
-    }
-
-    @Override
-    public void deleteBuffer(int bufferId) {
-        synchronized (mUnboundTextures) {
-            mDeleteBuffers.add(bufferId);
-        }
-    }
-
-    @Override
-    public void deleteRecycledResources() {
-        synchronized (mUnboundTextures) {
-            IntArray ids = mUnboundTextures;
-            if (mUnboundTextures.size() > 0) {
-                mGLId.glDeleteTextures(null, ids.size(), ids.getInternalArray(), 0);
-                ids.clear();
-            }
-
-            ids = mDeleteBuffers;
-            if (ids.size() > 0) {
-                mGLId.glDeleteBuffers(null, ids.size(), ids.getInternalArray(), 0);
-                ids.clear();
-            }
-        }
-    }
-
-    @Override
-    public void dumpStatisticsAndClear() {
-        String line = String.format("MESH:%d, TEX_RECT:%d, FILL_RECT:%d, LINE:%d", mCountDrawMesh,
-                mCountTextureRect, mCountFillRect, mCountDrawLine);
-        mCountDrawMesh = 0;
-        mCountTextureRect = 0;
-        mCountFillRect = 0;
-        mCountDrawLine = 0;
-        Log.d(TAG, line);
-    }
-
-    @Override
-    public void endRenderTarget() {
-        RawTexture oldTexture = mTargetTextures.remove(mTargetTextures.size() - 1);
-        RawTexture texture = getTargetTexture();
-        setRenderTarget(oldTexture, texture);
-        restore(); // restore matrix and alpha
-    }
-
-    @Override
-    public void beginRenderTarget(RawTexture texture) {
-        save(); // save matrix and alpha and blending
-        RawTexture oldTexture = getTargetTexture();
-        mTargetTextures.add(texture);
-        setRenderTarget(oldTexture, texture);
-    }
-
-    private RawTexture getTargetTexture() {
-        return mTargetTextures.get(mTargetTextures.size() - 1);
-    }
-
-    private void setRenderTarget(BasicTexture oldTexture, RawTexture texture) {
-        if (oldTexture == null && texture != null) {
-            GLES20.glGenFramebuffers(1, mFrameBuffer, 0);
-            checkError();
-            GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer[0]);
-            checkError();
-        } else if (oldTexture != null && texture == null) {
-            GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
-            checkError();
-            GLES20.glDeleteFramebuffers(1, mFrameBuffer, 0);
-            checkError();
-        }
-
-        if (texture == null) {
-            setSize(mScreenWidth, mScreenHeight);
-        } else {
-            setSize(texture.getWidth(), texture.getHeight());
-
-            if (!texture.isLoaded()) {
-                texture.prepare(this);
-            }
-
-            GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
-                    texture.getTarget(), texture.getId(), 0);
-            checkError();
-
-            checkFramebufferStatus();
-        }
-    }
-
-    private static void checkFramebufferStatus() {
-        int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
-        if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
-            String msg = "";
-            switch (status) {
-                case GLES20.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
-                    msg = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
-                    break;
-                case GLES20.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
-                    msg = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
-                    break;
-                case GLES20.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
-                    msg = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
-                    break;
-                case GLES20.GL_FRAMEBUFFER_UNSUPPORTED:
-                    msg = "GL_FRAMEBUFFER_UNSUPPORTED";
-                    break;
-            }
-            throw new RuntimeException(msg + ":" + Integer.toHexString(status));
-        }
-    }
-
-    @Override
-    public void setTextureParameters(BasicTexture texture) {
-        int target = texture.getTarget();
-        GLES20.glBindTexture(target, texture.getId());
-        checkError();
-        GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
-        GLES20.glTexParameteri(target, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
-        GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
-        GLES20.glTexParameterf(target, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
-    }
-
-    @Override
-    public void initializeTextureSize(BasicTexture texture, int format, int type) {
-        int target = texture.getTarget();
-        GLES20.glBindTexture(target, texture.getId());
-        checkError();
-        int width = texture.getTextureWidth();
-        int height = texture.getTextureHeight();
-        GLES20.glTexImage2D(target, 0, format, width, height, 0, format, type, null);
-    }
-
-    @Override
-    public void initializeTexture(BasicTexture texture, Bitmap bitmap) {
-        int target = texture.getTarget();
-        GLES20.glBindTexture(target, texture.getId());
-        checkError();
-        GLUtils.texImage2D(target, 0, bitmap, 0);
-    }
-
-    @Override
-    public void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, Bitmap bitmap,
-            int format, int type) {
-        int target = texture.getTarget();
-        GLES20.glBindTexture(target, texture.getId());
-        checkError();
-        GLUtils.texSubImage2D(target, 0, xOffset, yOffset, bitmap, format, type);
-    }
-
-    @Override
-    public int uploadBuffer(FloatBuffer buf) {
-        return uploadBuffer(buf, FLOAT_SIZE);
-    }
-
-    @Override
-    public int uploadBuffer(ByteBuffer buf) {
-        return uploadBuffer(buf, 1);
-    }
-
-    private int uploadBuffer(Buffer buffer, int elementSize) {
-        mGLId.glGenBuffers(1, mTempIntArray, 0);
-        checkError();
-        int bufferId = mTempIntArray[0];
-        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, bufferId);
-        checkError();
-        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * elementSize, buffer,
-                GLES20.GL_STATIC_DRAW);
-        checkError();
-        return bufferId;
-    }
-
-    public static void checkError() {
-        int error = GLES20.glGetError();
-        if (error != 0) {
-            Throwable t = new Throwable();
-            Log.e(TAG, "GL error: " + error, t);
-        }
-    }
-
-    @SuppressWarnings("unused")
-    private static void printMatrix(String message, float[] m, int offset) {
-        StringBuilder b = new StringBuilder(message);
-        for (int i = 0; i < MATRIX_SIZE; i++) {
-            b.append(' ');
-            if (i % 4 == 0) {
-                b.append('\n');
-            }
-            b.append(m[offset + i]);
-        }
-        Log.v(TAG, b.toString());
-    }
-
-    @Override
-    public void recoverFromLightCycle() {
-        GLES20.glViewport(0, 0, mWidth, mHeight);
-        GLES20.glDisable(GLES20.GL_DEPTH_TEST);
-        GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
-        checkError();
-    }
-
-    @Override
-    public void getBounds(Rect bounds, int x, int y, int width, int height) {
-        Matrix.translateM(mTempMatrix, 0, mMatrices, mCurrentMatrixIndex, x, y, 0f);
-        Matrix.scaleM(mTempMatrix, 0, width, height, 1f);
-        Matrix.multiplyMV(mTempMatrix, MATRIX_SIZE, mTempMatrix, 0, BOUNDS_COORDINATES, 0);
-        Matrix.multiplyMV(mTempMatrix, MATRIX_SIZE + 4, mTempMatrix, 0, BOUNDS_COORDINATES, 4);
-        bounds.left = Math.round(mTempMatrix[MATRIX_SIZE]);
-        bounds.right = Math.round(mTempMatrix[MATRIX_SIZE + 4]);
-        bounds.top = Math.round(mTempMatrix[MATRIX_SIZE + 1]);
-        bounds.bottom = Math.round(mTempMatrix[MATRIX_SIZE + 5]);
-        bounds.sort();
-    }
-
-    @Override
-    public GLId getGLId() {
-        return mGLId;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20IdImpl.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20IdImpl.java
deleted file mode 100644
index 6cd7149..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLES20IdImpl.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package com.android.gallery3d.glrenderer;
-
-import android.opengl.GLES20;
-
-import javax.microedition.khronos.opengles.GL11;
-import javax.microedition.khronos.opengles.GL11ExtensionPack;
-
-public class GLES20IdImpl implements GLId {
-    private final int[] mTempIntArray = new int[1];
-
-    @Override
-    public int generateTexture() {
-        GLES20.glGenTextures(1, mTempIntArray, 0);
-        GLES20Canvas.checkError();
-        return mTempIntArray[0];
-    }
-
-    @Override
-    public void glGenBuffers(int n, int[] buffers, int offset) {
-        GLES20.glGenBuffers(n, buffers, offset);
-        GLES20Canvas.checkError();
-    }
-
-    @Override
-    public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset) {
-        GLES20.glDeleteTextures(n, textures, offset);
-        GLES20Canvas.checkError();
-    }
-
-
-    @Override
-    public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset) {
-        GLES20.glDeleteBuffers(n, buffers, offset);
-        GLES20Canvas.checkError();
-    }
-
-    @Override
-    public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset) {
-        GLES20.glDeleteFramebuffers(n, buffers, offset);
-        GLES20Canvas.checkError();
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLId.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLId.java
deleted file mode 100644
index 3cec558..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLId.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.glrenderer;
-
-import javax.microedition.khronos.opengles.GL11;
-import javax.microedition.khronos.opengles.GL11ExtensionPack;
-
-// This mimics corresponding GL functions.
-public interface GLId {
-    public int generateTexture();
-
-    public void glGenBuffers(int n, int[] buffers, int offset);
-
-    public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset);
-
-    public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset);
-
-    public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset);
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLPaint.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLPaint.java
deleted file mode 100644
index b26e9ab..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/GLPaint.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.glrenderer;
-
-import com.android.gallery3d.common.Utils;
-
-public class GLPaint {
-    private float mLineWidth = 1f;
-    private int mColor = 0;
-
-    public void setColor(int color) {
-        mColor = color;
-    }
-
-    public int getColor() {
-        return mColor;
-    }
-
-    public void setLineWidth(float width) {
-        Utils.assertTrue(width >= 0);
-        mLineWidth = width;
-    }
-
-    public float getLineWidth() {
-        return mLineWidth;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/IntArray.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/IntArray.java
deleted file mode 100644
index f123624..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/IntArray.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.glrenderer;
-
-public class IntArray {
-    private static final int INIT_CAPACITY = 8;
-
-    private int mData[] = new int[INIT_CAPACITY];
-    private int mSize = 0;
-
-    public void add(int value) {
-        if (mData.length == mSize) {
-            int temp[] = new int[mSize + mSize];
-            System.arraycopy(mData, 0, temp, 0, mSize);
-            mData = temp;
-        }
-        mData[mSize++] = value;
-    }
-
-    public int removeLast() {
-        mSize--;
-        return mData[mSize];
-    }
-
-    public int size() {
-        return mSize;
-    }
-
-    // For testing only
-    public int[] toArray(int[] result) {
-        if (result == null || result.length < mSize) {
-            result = new int[mSize];
-        }
-        System.arraycopy(mData, 0, result, 0, mSize);
-        return result;
-    }
-
-    public int[] getInternalArray() {
-        return mData;
-    }
-
-    public void clear() {
-        mSize = 0;
-        if (mData.length != INIT_CAPACITY) mData = new int[INIT_CAPACITY];
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/RawTexture.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/RawTexture.java
deleted file mode 100644
index 93f0fdf..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/RawTexture.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.glrenderer;
-
-import android.util.Log;
-
-import javax.microedition.khronos.opengles.GL11;
-
-public class RawTexture extends BasicTexture {
-    private static final String TAG = "RawTexture";
-
-    private final boolean mOpaque;
-    private boolean mIsFlipped;
-
-    public RawTexture(int width, int height, boolean opaque) {
-        mOpaque = opaque;
-        setSize(width, height);
-    }
-
-    @Override
-    public boolean isOpaque() {
-        return mOpaque;
-    }
-
-    @Override
-    public boolean isFlippedVertically() {
-        return mIsFlipped;
-    }
-
-    public void setIsFlippedVertically(boolean isFlipped) {
-        mIsFlipped = isFlipped;
-    }
-
-    protected void prepare(GLCanvas canvas) {
-        GLId glId = canvas.getGLId();
-        mId = glId.generateTexture();
-        canvas.initializeTextureSize(this, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE);
-        canvas.setTextureParameters(this);
-        mState = STATE_LOADED;
-        setAssociatedCanvas(canvas);
-    }
-
-    @Override
-    protected boolean onBind(GLCanvas canvas) {
-        if (isLoaded()) return true;
-        Log.w(TAG, "lost the content due to context change");
-        return false;
-    }
-
-    @Override
-     public void yield() {
-         // we cannot free the texture because we have no backup.
-     }
-
-    @Override
-    protected int getTarget() {
-        return GL11.GL_TEXTURE_2D;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/Texture.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/Texture.java
deleted file mode 100644
index 3dcae4a..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/Texture.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.glrenderer;
-
-
-// Texture is a rectangular image which can be drawn on GLCanvas.
-// The isOpaque() function gives a hint about whether the texture is opaque,
-// so the drawing can be done faster.
-//
-// This is the current texture hierarchy:
-//
-// Texture
-// -- ColorTexture
-// -- FadeInTexture
-// -- BasicTexture
-//    -- UploadedTexture
-//       -- BitmapTexture
-//       -- Tile
-//       -- ResourceTexture
-//          -- NinePatchTexture
-//       -- CanvasTexture
-//          -- StringTexture
-//
-public interface Texture {
-    public int getWidth();
-    public int getHeight();
-    public void draw(GLCanvas canvas, int x, int y);
-    public void draw(GLCanvas canvas, int x, int y, int w, int h);
-    public boolean isOpaque();
-}
diff --git a/WallpaperPicker/src/com/android/gallery3d/glrenderer/UploadedTexture.java b/WallpaperPicker/src/com/android/gallery3d/glrenderer/UploadedTexture.java
deleted file mode 100644
index 8075bf8..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/glrenderer/UploadedTexture.java
+++ /dev/null
@@ -1,299 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.glrenderer;
-
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.opengl.GLUtils;
-
-import com.android.gallery3d.common.Utils;
-import com.android.launcher3.util.Thunk;
-
-import java.util.HashMap;
-
-import javax.microedition.khronos.opengles.GL11;
-
-// UploadedTextures use a Bitmap for the content of the texture.
-//
-// Subclasses should implement onGetBitmap() to provide the Bitmap and
-// implement onFreeBitmap(mBitmap) which will be called when the Bitmap
-// is not needed anymore.
-//
-// isContentValid() is meaningful only when the isLoaded() returns true.
-// It means whether the content needs to be updated.
-//
-// The user of this class should call recycle() when the texture is not
-// needed anymore.
-//
-// By default an UploadedTexture is opaque (so it can be drawn faster without
-// blending). The user or subclass can override it using setOpaque().
-public abstract class UploadedTexture extends BasicTexture {
-
-    // To prevent keeping allocation the borders, we store those used borders here.
-    // Since the length will be power of two, it won't use too much memory.
-    private static HashMap<BorderKey, Bitmap> sBorderLines =
-            new HashMap<BorderKey, Bitmap>();
-    private static BorderKey sBorderKey = new BorderKey();
-
-    @SuppressWarnings("unused")
-    private static final String TAG = "Texture";
-    private boolean mContentValid = true;
-
-    // indicate this textures is being uploaded in background
-    private boolean mIsUploading = false;
-    private boolean mOpaque = true;
-    private boolean mThrottled = false;
-    private static int sUploadedCount;
-    private static final int UPLOAD_LIMIT = 100;
-
-    protected Bitmap mBitmap;
-    private int mBorder;
-
-    protected UploadedTexture() {
-        this(false);
-    }
-
-    protected UploadedTexture(boolean hasBorder) {
-        super(null, 0, STATE_UNLOADED);
-        if (hasBorder) {
-            setBorder(true);
-            mBorder = 1;
-        }
-    }
-
-    protected void setIsUploading(boolean uploading) {
-        mIsUploading = uploading;
-    }
-
-    public boolean isUploading() {
-        return mIsUploading;
-    }
-
-    @Thunk static class BorderKey implements Cloneable {
-        public boolean vertical;
-        public Config config;
-        public int length;
-
-        @Override
-        public int hashCode() {
-            int x = config.hashCode() ^ length;
-            return vertical ? x : -x;
-        }
-
-        @Override
-        public boolean equals(Object object) {
-            if (!(object instanceof BorderKey)) return false;
-            BorderKey o = (BorderKey) object;
-            return vertical == o.vertical
-                    && config == o.config && length == o.length;
-        }
-
-        @Override
-        public BorderKey clone() {
-            try {
-                return (BorderKey) super.clone();
-            } catch (CloneNotSupportedException e) {
-                throw new AssertionError(e);
-            }
-        }
-    }
-
-    protected void setThrottled(boolean throttled) {
-        mThrottled = throttled;
-    }
-
-    private static Bitmap getBorderLine(
-            boolean vertical, Config config, int length) {
-        BorderKey key = sBorderKey;
-        key.vertical = vertical;
-        key.config = config;
-        key.length = length;
-        Bitmap bitmap = sBorderLines.get(key);
-        if (bitmap == null) {
-            bitmap = vertical
-                    ? Bitmap.createBitmap(1, length, config)
-                    : Bitmap.createBitmap(length, 1, config);
-            sBorderLines.put(key.clone(), bitmap);
-        }
-        return bitmap;
-    }
-
-    private Bitmap getBitmap() {
-        if (mBitmap == null) {
-            mBitmap = onGetBitmap();
-            int w = mBitmap.getWidth() + mBorder * 2;
-            int h = mBitmap.getHeight() + mBorder * 2;
-            if (mWidth == UNSPECIFIED) {
-                setSize(w, h);
-            }
-        }
-        return mBitmap;
-    }
-
-    private void freeBitmap() {
-        Utils.assertTrue(mBitmap != null);
-        onFreeBitmap(mBitmap);
-        mBitmap = null;
-    }
-
-    @Override
-    public int getWidth() {
-        if (mWidth == UNSPECIFIED) getBitmap();
-        return mWidth;
-    }
-
-    @Override
-    public int getHeight() {
-        if (mWidth == UNSPECIFIED) getBitmap();
-        return mHeight;
-    }
-
-    protected abstract Bitmap onGetBitmap();
-
-    protected abstract void onFreeBitmap(Bitmap bitmap);
-
-    protected void invalidateContent() {
-        if (mBitmap != null) freeBitmap();
-        mContentValid = false;
-        mWidth = UNSPECIFIED;
-        mHeight = UNSPECIFIED;
-    }
-
-    /**
-     * Whether the content on GPU is valid.
-     */
-    public boolean isContentValid() {
-        return isLoaded() && mContentValid;
-    }
-
-    /**
-     * Updates the content on GPU's memory.
-     * @param canvas
-     */
-    public void updateContent(GLCanvas canvas) {
-        if (!isLoaded()) {
-            if (mThrottled && ++sUploadedCount > UPLOAD_LIMIT) {
-                return;
-            }
-            uploadToCanvas(canvas);
-        } else if (!mContentValid) {
-            Bitmap bitmap = getBitmap();
-            int format = GLUtils.getInternalFormat(bitmap);
-            int type = GLUtils.getType(bitmap);
-            canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type);
-            freeBitmap();
-            mContentValid = true;
-        }
-    }
-
-    public static void resetUploadLimit() {
-        sUploadedCount = 0;
-    }
-
-    public static boolean uploadLimitReached() {
-        return sUploadedCount > UPLOAD_LIMIT;
-    }
-
-    private void uploadToCanvas(GLCanvas canvas) {
-
-        Bitmap bitmap = getBitmap();
-        if (bitmap != null) {
-            try {
-                int bWidth = bitmap.getWidth();
-                int bHeight = bitmap.getHeight();
-                int width = bWidth + mBorder * 2;
-                int height = bHeight + mBorder * 2;
-                int texWidth = getTextureWidth();
-                int texHeight = getTextureHeight();
-
-                Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
-
-                // Upload the bitmap to a new texture.
-                mId = canvas.getGLId().generateTexture();
-                canvas.setTextureParameters(this);
-
-                if (bWidth == texWidth && bHeight == texHeight) {
-                    canvas.initializeTexture(this, bitmap);
-                } else {
-                    int format = GLUtils.getInternalFormat(bitmap);
-                    int type = GLUtils.getType(bitmap);
-                    Config config = bitmap.getConfig();
-
-                    canvas.initializeTextureSize(this, format, type);
-                    canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type);
-
-                    if (mBorder > 0) {
-                        // Left border
-                        Bitmap line = getBorderLine(true, config, texHeight);
-                        canvas.texSubImage2D(this, 0, 0, line, format, type);
-
-                        // Top border
-                        line = getBorderLine(false, config, texWidth);
-                        canvas.texSubImage2D(this, 0, 0, line, format, type);
-                    }
-
-                    // Right border
-                    if (mBorder + bWidth < texWidth) {
-                        Bitmap line = getBorderLine(true, config, texHeight);
-                        canvas.texSubImage2D(this, mBorder + bWidth, 0, line, format, type);
-                    }
-
-                    // Bottom border
-                    if (mBorder + bHeight < texHeight) {
-                        Bitmap line = getBorderLine(false, config, texWidth);
-                        canvas.texSubImage2D(this, 0, mBorder + bHeight, line, format, type);
-                    }
-                }
-            } finally {
-                freeBitmap();
-            }
-            // Update texture state.
-            setAssociatedCanvas(canvas);
-            mState = STATE_LOADED;
-            mContentValid = true;
-        } else {
-            mState = STATE_ERROR;
-            throw new RuntimeException("Texture load fail, no bitmap");
-        }
-    }
-
-    @Override
-    protected boolean onBind(GLCanvas canvas) {
-        updateContent(canvas);
-        return isContentValid();
-    }
-
-    @Override
-    protected int getTarget() {
-        return GL11.GL_TEXTURE_2D;
-    }
-
-    public void setOpaque(boolean isOpaque) {
-        mOpaque = isOpaque;
-    }
-
-    @Override
-    public boolean isOpaque() {
-        return mOpaque;
-    }
-
-    @Override
-    public void recycle() {
-        super.recycle();
-        if (mBitmap != null) freeBitmap();
-    }
-}
diff --git a/WallpaperPicker/src/com/android/launcher3/AlphaDisableableButton.java b/WallpaperPicker/src/com/android/launcher3/AlphaDisableableButton.java
deleted file mode 100644
index f0796c3..0000000
--- a/WallpaperPicker/src/com/android/launcher3/AlphaDisableableButton.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.launcher3;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.Button;
-
-/**
- * A Button which becomes translucent when it is disabled
- */
-public class AlphaDisableableButton extends Button {
-    public static float DISABLED_ALPHA_VALUE = 0.4f;
-    public AlphaDisableableButton(Context context) {
-        this(context, null);
-    }
-
-    public AlphaDisableableButton(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public AlphaDisableableButton(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        setLayerType(LAYER_TYPE_HARDWARE, null);
-    }
-
-    @Override
-    public void setEnabled(boolean enabled) {
-        super.setEnabled(enabled);
-        if(enabled) {
-            setAlpha(1.0f);
-        } else {
-            setAlpha(DISABLED_ALPHA_VALUE);
-        }
-    }
-}
diff --git a/WallpaperPicker/src/com/android/launcher3/CheckableFrameLayout.java b/WallpaperPicker/src/com/android/launcher3/CheckableFrameLayout.java
deleted file mode 100644
index 5b7d824..0000000
--- a/WallpaperPicker/src/com/android/launcher3/CheckableFrameLayout.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.widget.Checkable;
-import android.widget.FrameLayout;
-
-public class CheckableFrameLayout extends FrameLayout implements Checkable {
-    private static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked };
-    boolean mChecked;
-
-    public CheckableFrameLayout(Context context) {
-        super(context);
-    }
-
-    public CheckableFrameLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public CheckableFrameLayout(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    public boolean isChecked() {
-        return mChecked;
-    }
-
-    public void setChecked(boolean checked) {
-        if (checked != mChecked) {
-            mChecked = checked;
-            refreshDrawableState();
-        }
-    }
-
-    public void toggle() {
-        setChecked(!mChecked);
-    }
-
-    @Override
-    protected int[] onCreateDrawableState(int extraSpace) {
-        final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
-        if (isChecked()) {
-            mergeDrawableStates(drawableState, CHECKED_STATE_SET);
-        }
-        return drawableState;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/launcher3/CropView.java b/WallpaperPicker/src/com/android/launcher3/CropView.java
deleted file mode 100644
index c1578c1..0000000
--- a/WallpaperPicker/src/com/android/launcher3/CropView.java
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.Context;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.RectF;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
-import android.view.ScaleGestureDetector.OnScaleGestureListener;
-import android.view.ViewConfiguration;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-
-import com.android.photos.views.TiledImageRenderer.TileSource;
-import com.android.photos.views.TiledImageView;
-
-public class  CropView extends TiledImageView implements OnScaleGestureListener {
-
-    private ScaleGestureDetector mScaleGestureDetector;
-    private long mTouchDownTime;
-    private float mFirstX, mFirstY;
-    private float mLastX, mLastY;
-    private float mCenterX, mCenterY;
-    private float mMinScale;
-    private boolean mTouchEnabled = true;
-    private RectF mTempEdges = new RectF();
-    private float[] mTempPoint = new float[] { 0, 0 };
-    private float[] mTempCoef = new float[] { 0, 0 };
-    private float[] mTempAdjustment = new float[] { 0, 0 };
-    private float[] mTempImageDims = new float[] { 0, 0 };
-    private float[] mTempRendererCenter = new float[] { 0, 0 };
-    TouchCallback mTouchCallback;
-    Matrix mRotateMatrix;
-    Matrix mInverseRotateMatrix;
-
-    public interface TouchCallback {
-        void onTouchDown();
-        void onTap();
-        void onTouchUp();
-    }
-
-    public CropView(Context context) {
-        this(context, null);
-    }
-
-    public CropView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mScaleGestureDetector = new ScaleGestureDetector(context, this);
-        mRotateMatrix = new Matrix();
-        mInverseRotateMatrix = new Matrix();
-    }
-
-    private float[] getImageDims() {
-        final float imageWidth = mRenderer.source.getImageWidth();
-        final float imageHeight = mRenderer.source.getImageHeight();
-        float[] imageDims = mTempImageDims;
-        imageDims[0] = imageWidth;
-        imageDims[1] = imageHeight;
-        mRotateMatrix.mapPoints(imageDims);
-        imageDims[0] = Math.abs(imageDims[0]);
-        imageDims[1] = Math.abs(imageDims[1]);
-        return imageDims;
-    }
-
-    private void getEdgesHelper(RectF edgesOut) {
-        final float width = getWidth();
-        final float height = getHeight();
-        final float[] imageDims = getImageDims();
-        final float imageWidth = imageDims[0];
-        final float imageHeight = imageDims[1];
-
-        float initialCenterX = mRenderer.source.getImageWidth() / 2f;
-        float initialCenterY = mRenderer.source.getImageHeight() / 2f;
-
-        float[] rendererCenter = mTempRendererCenter;
-        rendererCenter[0] = mCenterX - initialCenterX;
-        rendererCenter[1] = mCenterY - initialCenterY;
-        mRotateMatrix.mapPoints(rendererCenter);
-        rendererCenter[0] += imageWidth / 2;
-        rendererCenter[1] += imageHeight / 2;
-
-        final float scale = mRenderer.scale;
-        float centerX = (width / 2f - rendererCenter[0] + (imageWidth - width) / 2f)
-                * scale + width / 2f;
-        float centerY = (height / 2f - rendererCenter[1] + (imageHeight - height) / 2f)
-                * scale + height / 2f;
-        float leftEdge = centerX - imageWidth / 2f * scale;
-        float rightEdge = centerX + imageWidth / 2f * scale;
-        float topEdge = centerY - imageHeight / 2f * scale;
-        float bottomEdge = centerY + imageHeight / 2f * scale;
-
-        edgesOut.left = leftEdge;
-        edgesOut.right = rightEdge;
-        edgesOut.top = topEdge;
-        edgesOut.bottom = bottomEdge;
-    }
-
-    public int getImageRotation() {
-        return mRenderer.rotation;
-    }
-
-    public RectF getCrop() {
-        final RectF edges = mTempEdges;
-        getEdgesHelper(edges);
-        final float scale = mRenderer.scale;
-
-        float cropLeft = -edges.left / scale;
-        float cropTop = -edges.top / scale;
-        float cropRight = cropLeft + getWidth() / scale;
-        float cropBottom = cropTop + getHeight() / scale;
-
-        return new RectF(cropLeft, cropTop, cropRight, cropBottom);
-    }
-
-    public Point getSourceDimensions() {
-        return new Point(mRenderer.source.getImageWidth(), mRenderer.source.getImageHeight());
-    }
-
-    public void setTileSource(TileSource source, Runnable isReadyCallback) {
-        super.setTileSource(source, isReadyCallback);
-        mCenterX = mRenderer.centerX;
-        mCenterY = mRenderer.centerY;
-        mRotateMatrix.reset();
-        mRotateMatrix.setRotate(mRenderer.rotation);
-        mInverseRotateMatrix.reset();
-        mInverseRotateMatrix.setRotate(-mRenderer.rotation);
-        updateMinScale(getWidth(), getHeight(), source, true);
-    }
-
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        updateMinScale(w, h, mRenderer.source, false);
-    }
-
-    public void setScaleAndCenter(float scale, float x, float y) {
-        synchronized (mLock) {
-            mRenderer.scale = scale;
-            mCenterX = x;
-            mCenterY = y;
-            updateCenter();
-        }
-    }
-
-    public float getScale() {
-        return mRenderer.scale;
-    }
-
-    private void updateMinScale(int w, int h, TileSource source, boolean resetScale) {
-        synchronized (mLock) {
-            if (resetScale) {
-                mRenderer.scale = 1;
-            }
-            if (source != null) {
-                final float[] imageDims = getImageDims();
-                final float imageWidth = imageDims[0];
-                final float imageHeight = imageDims[1];
-                mMinScale = Math.max(w / imageWidth, h / imageHeight);
-                mRenderer.scale =
-                        Math.max(mMinScale, resetScale ? Float.MIN_VALUE : mRenderer.scale);
-            }
-        }
-    }
-
-    @Override
-    public boolean onScaleBegin(ScaleGestureDetector detector) {
-        return true;
-    }
-
-    @Override
-    public boolean onScale(ScaleGestureDetector detector) {
-        // Don't need the lock because this will only fire inside of
-        // onTouchEvent
-        mRenderer.scale *= detector.getScaleFactor();
-        mRenderer.scale = Math.max(mMinScale, mRenderer.scale);
-        invalidate();
-        return true;
-    }
-
-    @Override
-    public void onScaleEnd(ScaleGestureDetector detector) {
-    }
-
-    public void moveToLeft() {
-        if (getWidth() == 0 || getHeight() == 0) {
-            final ViewTreeObserver observer = getViewTreeObserver();
-            observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
-                    public void onGlobalLayout() {
-                        moveToLeft();
-                        getViewTreeObserver().removeOnGlobalLayoutListener(this);
-                    }
-                });
-        }
-        final RectF edges = mTempEdges;
-        getEdgesHelper(edges);
-        final float scale = mRenderer.scale;
-        mCenterX += Math.ceil(edges.left / scale);
-        updateCenter();
-    }
-
-    private void updateCenter() {
-        mRenderer.centerX = Math.round(mCenterX);
-        mRenderer.centerY = Math.round(mCenterY);
-    }
-
-    public PointF getCenter() {
-        return new PointF(mCenterX, mCenterY);
-    }
-
-    public void setTouchEnabled(boolean enabled) {
-        mTouchEnabled = enabled;
-    }
-
-    public void setTouchCallback(TouchCallback cb) {
-        mTouchCallback = cb;
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        int action = event.getActionMasked();
-        final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
-        final int skipIndex = pointerUp ? event.getActionIndex() : -1;
-
-        // Determine focal point
-        float sumX = 0, sumY = 0;
-        final int count = event.getPointerCount();
-        for (int i = 0; i < count; i++) {
-            if (skipIndex == i)
-                continue;
-            sumX += event.getX(i);
-            sumY += event.getY(i);
-        }
-        final int div = pointerUp ? count - 1 : count;
-        float x = sumX / div;
-        float y = sumY / div;
-
-        if (action == MotionEvent.ACTION_DOWN) {
-            mFirstX = x;
-            mFirstY = y;
-            mTouchDownTime = System.currentTimeMillis();
-            if (mTouchCallback != null) {
-                mTouchCallback.onTouchDown();
-            }
-        } else if (action == MotionEvent.ACTION_UP) {
-            ViewConfiguration config = ViewConfiguration.get(getContext());
-
-            float squaredDist = (mFirstX - x) * (mFirstX - x) + (mFirstY - y) * (mFirstY - y);
-            float slop = config.getScaledTouchSlop() * config.getScaledTouchSlop();
-            long now = System.currentTimeMillis();
-            if (mTouchCallback != null) {
-                // only do this if it's a small movement
-                if (squaredDist < slop &&
-                        now < mTouchDownTime + ViewConfiguration.getTapTimeout()) {
-                    mTouchCallback.onTap();
-                }
-                mTouchCallback.onTouchUp();
-            }
-        }
-
-        if (!mTouchEnabled) {
-            return true;
-        }
-
-        synchronized (mLock) {
-            mScaleGestureDetector.onTouchEvent(event);
-            switch (action) {
-                case MotionEvent.ACTION_MOVE:
-                    float[] point = mTempPoint;
-                    point[0] = (mLastX - x) / mRenderer.scale;
-                    point[1] = (mLastY - y) / mRenderer.scale;
-                    mInverseRotateMatrix.mapPoints(point);
-                    mCenterX += point[0];
-                    mCenterY += point[1];
-                    updateCenter();
-                    invalidate();
-                    break;
-            }
-            if (mRenderer.source != null) {
-                // Adjust position so that the wallpaper covers the entire area
-                // of the screen
-                final RectF edges = mTempEdges;
-                getEdgesHelper(edges);
-                final float scale = mRenderer.scale;
-
-                float[] coef = mTempCoef;
-                coef[0] = 1;
-                coef[1] = 1;
-                mRotateMatrix.mapPoints(coef);
-                float[] adjustment = mTempAdjustment;
-                mTempAdjustment[0] = 0;
-                mTempAdjustment[1] = 0;
-                if (edges.left > 0) {
-                    adjustment[0] = edges.left / scale;
-                } else if (edges.right < getWidth()) {
-                    adjustment[0] = (edges.right - getWidth()) / scale;
-                }
-                if (edges.top > 0) {
-                    adjustment[1] = (float) Math.ceil(edges.top / scale);
-                } else if (edges.bottom < getHeight()) {
-                    adjustment[1] = (edges.bottom - getHeight()) / scale;
-                }
-                for (int dim = 0; dim <= 1; dim++) {
-                    if (coef[dim] > 0) adjustment[dim] = (float) Math.ceil(adjustment[dim]);
-                }
-
-                mInverseRotateMatrix.mapPoints(adjustment);
-                mCenterX += adjustment[0];
-                mCenterY += adjustment[1];
-                updateCenter();
-            }
-        }
-
-        mLastX = x;
-        mLastY = y;
-        return true;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/launcher3/DrawableTileSource.java b/WallpaperPicker/src/com/android/launcher3/DrawableTileSource.java
deleted file mode 100644
index c1f2eff..0000000
--- a/WallpaperPicker/src/com/android/launcher3/DrawableTileSource.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-
-import com.android.gallery3d.glrenderer.BasicTexture;
-import com.android.gallery3d.glrenderer.BitmapTexture;
-import com.android.photos.views.TiledImageRenderer;
-
-public class DrawableTileSource implements TiledImageRenderer.TileSource {
-    private static final int GL_SIZE_LIMIT = 2048;
-    // This must be no larger than half the size of the GL_SIZE_LIMIT
-    // due to decodePreview being allowed to be up to 2x the size of the target
-    public static final int MAX_PREVIEW_SIZE = GL_SIZE_LIMIT / 2;
-
-    private int mTileSize;
-    private int mPreviewSize;
-    private Drawable mDrawable;
-    private BitmapTexture mPreview;
-
-    public DrawableTileSource(Context context, Drawable d, int previewSize) {
-        mTileSize = TiledImageRenderer.suggestedTileSize(context);
-        mDrawable = d;
-        mPreviewSize = Math.min(previewSize, MAX_PREVIEW_SIZE);
-    }
-
-    @Override
-    public int getTileSize() {
-        return mTileSize;
-    }
-
-    @Override
-    public int getImageWidth() {
-        return mDrawable.getIntrinsicWidth();
-    }
-
-    @Override
-    public int getImageHeight() {
-        return mDrawable.getIntrinsicHeight();
-    }
-
-    @Override
-    public int getRotation() {
-        return 0;
-    }
-
-    @Override
-    public BasicTexture getPreview() {
-        if (mPreviewSize == 0) {
-            return null;
-        }
-        if (mPreview == null){
-            float width = getImageWidth();
-            float height = getImageHeight();
-            while (width > MAX_PREVIEW_SIZE || height > MAX_PREVIEW_SIZE) {
-                width /= 2;
-                height /= 2;
-            }
-            Bitmap b = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
-            Canvas c = new Canvas(b);
-            mDrawable.setBounds(new Rect(0, 0, (int) width, (int) height));
-            mDrawable.draw(c);
-            c.setBitmap(null);
-            mPreview = new BitmapTexture(b);
-        }
-        return mPreview;
-    }
-
-    @Override
-    public Bitmap getTile(int level, int x, int y, Bitmap bitmap) {
-        int tileSize = getTileSize();
-        if (bitmap == null) {
-            bitmap = Bitmap.createBitmap(tileSize, tileSize, Bitmap.Config.ARGB_8888);
-        }
-        Canvas c = new Canvas(bitmap);
-        Rect bounds = new Rect(0, 0, getImageWidth(), getImageHeight());
-        bounds.offset(-x, -y);
-        mDrawable.setBounds(bounds);
-        mDrawable.draw(c);
-        c.setBitmap(null);
-        return bitmap;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/launcher3/LauncherWallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/LauncherWallpaperPickerActivity.java
deleted file mode 100644
index 091c054..0000000
--- a/WallpaperPicker/src/com/android/launcher3/LauncherWallpaperPickerActivity.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-// TODO: Remove this class
-public class LauncherWallpaperPickerActivity extends WallpaperPickerActivity {
-}
\ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java b/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java
deleted file mode 100644
index b53fce1..0000000
--- a/WallpaperPicker/src/com/android/launcher3/LiveWallpaperListAdapter.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.app.WallpaperInfo;
-import android.app.WallpaperManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
-import android.service.wallpaper.WallpaperService;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-
-import com.android.launcher3.util.Thunk;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-import java.text.Collator;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-public class LiveWallpaperListAdapter extends BaseAdapter implements ListAdapter {
-    private static final String LOG_TAG = "LiveWallpaperListAdapter";
-
-    private final LayoutInflater mInflater;
-    private final PackageManager mPackageManager;
-
-    @Thunk List<LiveWallpaperTile> mWallpapers;
-
-    @SuppressWarnings("unchecked")
-    public LiveWallpaperListAdapter(Context context) {
-        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        mPackageManager = context.getPackageManager();
-
-        List<ResolveInfo> list = mPackageManager.queryIntentServices(
-                new Intent(WallpaperService.SERVICE_INTERFACE),
-                PackageManager.GET_META_DATA);
-
-        mWallpapers = new ArrayList<LiveWallpaperTile>();
-
-        new LiveWallpaperEnumerator(context).execute(list);
-    }
-
-    public int getCount() {
-        if (mWallpapers == null) {
-            return 0;
-        }
-        return mWallpapers.size();
-    }
-
-    public LiveWallpaperTile getItem(int position) {
-        return mWallpapers.get(position);
-    }
-
-    public long getItemId(int position) {
-        return position;
-    }
-
-    public View getView(int position, View convertView, ViewGroup parent) {
-        View view;
-
-        if (convertView == null) {
-            view = mInflater.inflate(R.layout.wallpaper_picker_live_wallpaper_item, parent, false);
-        } else {
-            view = convertView;
-        }
-
-        LiveWallpaperTile wallpaperInfo = mWallpapers.get(position);
-        wallpaperInfo.setView(view);
-        ImageView image = (ImageView) view.findViewById(R.id.wallpaper_image);
-        ImageView icon = (ImageView) view.findViewById(R.id.wallpaper_icon);
-        if (wallpaperInfo.mThumbnail != null) {
-            image.setImageDrawable(wallpaperInfo.mThumbnail);
-            icon.setVisibility(View.GONE);
-        } else {
-            icon.setImageDrawable(wallpaperInfo.mInfo.loadIcon(mPackageManager));
-            icon.setVisibility(View.VISIBLE);
-        }
-
-        TextView label = (TextView) view.findViewById(R.id.wallpaper_item_label);
-        label.setText(wallpaperInfo.mInfo.loadLabel(mPackageManager));
-
-        return view;
-    }
-
-    public static class LiveWallpaperTile extends WallpaperPickerActivity.WallpaperTileInfo {
-        @Thunk Drawable mThumbnail;
-        @Thunk WallpaperInfo mInfo;
-        public LiveWallpaperTile(Drawable thumbnail, WallpaperInfo info, Intent intent) {
-            mThumbnail = thumbnail;
-            mInfo = info;
-        }
-        @Override
-        public void onClick(WallpaperPickerActivity a) {
-            Intent preview = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
-            preview.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT,
-                    mInfo.getComponent());
-            a.startActivityForResultSafely(preview,
-                    WallpaperPickerActivity.PICK_WALLPAPER_THIRD_PARTY_ACTIVITY);
-        }
-    }
-
-    private class LiveWallpaperEnumerator extends
-            AsyncTask<List<ResolveInfo>, LiveWallpaperTile, Void> {
-        private Context mContext;
-        private int mWallpaperPosition;
-
-        public LiveWallpaperEnumerator(Context context) {
-            super();
-            mContext = context;
-            mWallpaperPosition = 0;
-        }
-
-        @Override
-        protected Void doInBackground(List<ResolveInfo>... params) {
-            final PackageManager packageManager = mContext.getPackageManager();
-
-            List<ResolveInfo> list = params[0];
-
-            Collections.sort(list, new Comparator<ResolveInfo>() {
-                final Collator mCollator;
-
-                {
-                    mCollator = Collator.getInstance();
-                }
-
-                public int compare(ResolveInfo info1, ResolveInfo info2) {
-                    return mCollator.compare(info1.loadLabel(packageManager),
-                            info2.loadLabel(packageManager));
-                }
-            });
-
-            for (ResolveInfo resolveInfo : list) {
-                WallpaperInfo info = null;
-                try {
-                    info = new WallpaperInfo(mContext, resolveInfo);
-                } catch (XmlPullParserException e) {
-                    Log.w(LOG_TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e);
-                    continue;
-                } catch (IOException e) {
-                    Log.w(LOG_TAG, "Skipping wallpaper " + resolveInfo.serviceInfo, e);
-                    continue;
-                }
-
-
-                Drawable thumb = info.loadThumbnail(packageManager);
-                Intent launchIntent = new Intent(WallpaperService.SERVICE_INTERFACE);
-                launchIntent.setClassName(info.getPackageName(), info.getServiceName());
-                LiveWallpaperTile wallpaper = new LiveWallpaperTile(thumb, info, launchIntent);
-                publishProgress(wallpaper);
-            }
-            // Send a null object to show loading is finished
-            publishProgress((LiveWallpaperTile) null);
-
-            return null;
-        }
-
-        @Override
-        protected void onProgressUpdate(LiveWallpaperTile...infos) {
-            for (LiveWallpaperTile info : infos) {
-                if (info == null) {
-                    LiveWallpaperListAdapter.this.notifyDataSetChanged();
-                    break;
-                }
-                if (info.mThumbnail != null) {
-                    info.mThumbnail.setDither(true);
-                }
-                if (mWallpaperPosition < mWallpapers.size()) {
-                    mWallpapers.set(mWallpaperPosition, info);
-                } else {
-                    mWallpapers.add(info);
-                }
-                mWallpaperPosition++;
-            }
-        }
-    }
-}
diff --git a/WallpaperPicker/src/com/android/launcher3/NycWallpaperUtils.java b/WallpaperPicker/src/com/android/launcher3/NycWallpaperUtils.java
deleted file mode 100644
index abac830..0000000
--- a/WallpaperPicker/src/com/android/launcher3/NycWallpaperUtils.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.android.launcher3;
-
-import android.app.AlertDialog;
-import android.app.WallpaperManager;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.graphics.Rect;
-import android.os.AsyncTask;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * Utility class used to help set lockscreen wallpapers on N+.
- */
-public class NycWallpaperUtils {
-    public static final int FLAG_SET_SYSTEM = 1 << 0; // TODO: use WallpaperManager.FLAG_SET_SYSTEM
-    public static final int FLAG_SET_LOCK = 1 << 1; // TODO: use WallpaperManager.FLAG_SET_LOCK
-
-    /**
-     * Calls cropTask.execute(), once the user has selected which wallpaper to set. On pre-N
-     * devices, the prompt is not displayed since there is no API to set the lockscreen wallpaper.
-     */
-    public static void executeCropTaskAfterPrompt(
-            Context context, final AsyncTask<Integer, ?, ?> cropTask,
-            DialogInterface.OnCancelListener onCancelListener) {
-        if (Utilities.isNycOrAbove()) {
-            new AlertDialog.Builder(context)
-                    .setTitle(R.string.wallpaper_instructions)
-                    .setItems(R.array.which_wallpaper_options, new DialogInterface.OnClickListener() {
-                        @Override
-                        public void onClick(DialogInterface dialog, int selectedItemIndex) {
-                            int whichWallpaper;
-                            if (selectedItemIndex == 0) {
-                                whichWallpaper = FLAG_SET_SYSTEM;
-                            } else if (selectedItemIndex == 1) {
-                                whichWallpaper = FLAG_SET_LOCK;
-                            } else {
-                                whichWallpaper = FLAG_SET_SYSTEM | FLAG_SET_LOCK;
-                            }
-                            cropTask.execute(whichWallpaper);
-                        }
-                    })
-                    .setOnCancelListener(onCancelListener)
-                    .show();
-        } else {
-            cropTask.execute(FLAG_SET_SYSTEM);
-        }
-    }
-
-    public static void setStream(Context context, final InputStream data, Rect visibleCropHint,
-            boolean allowBackup, int whichWallpaper) throws IOException {
-        WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
-        try {
-            // TODO: use mWallpaperManager.setStream(data, visibleCropHint, allowBackup, which)
-            // without needing reflection.
-            Method setStream = WallpaperManager.class.getMethod("setStream", InputStream.class,
-                    Rect.class, boolean.class, int.class);
-            setStream.invoke(wallpaperManager, data, visibleCropHint, allowBackup, whichWallpaper);
-        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
-            // Fall back to previous implementation (set system)
-            wallpaperManager.setStream(data);
-        }
-    }
-
-    public static void clear(Context context, int whichWallpaper) throws IOException {
-        WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
-        try {
-            // TODO: use mWallpaperManager.clear(whichWallpaper) without needing reflection.
-            Method clear = WallpaperManager.class.getMethod("clear", int.class);
-            clear.invoke(wallpaperManager, whichWallpaper);
-        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
-            // Fall back to previous implementation (clear system)
-            wallpaperManager.clear();
-        }
-    }
-}
\ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
deleted file mode 100644
index 34226f7..0000000
--- a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ListAdapter;
-
-import com.android.launcher3.WallpaperCropActivity.CropViewScaleAndOffsetProvider;
-import com.android.photos.views.TiledImageRenderer.TileSource;
-
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-
-
-public class SavedWallpaperImages extends BaseAdapter implements ListAdapter {
-    private static String TAG = "Launcher3.SavedWallpaperImages";
-    private ImageDb mDb;
-    ArrayList<SavedWallpaperTile> mImages;
-    Context mContext;
-    LayoutInflater mLayoutInflater;
-
-    public static class SavedWallpaperTile extends WallpaperPickerActivity.FileWallpaperInfo {
-        private int mDbId;
-
-        // three floats representing scale, centerX and centerY of the crop view in order.
-        private Float[] mExtras;
-        public SavedWallpaperTile(int dbId, File target, Drawable thumb, Float[] extras) {
-            super(target, thumb);
-            mDbId = dbId;
-            mExtras = extras != null && extras.length == 3 ? extras : null;
-        }
-
-        @Override
-        public void onDelete(WallpaperPickerActivity a) {
-            a.getSavedImages().deleteImage(mDbId);
-        }
-
-        @Override
-        protected CropViewScaleAndOffsetProvider getCropViewScaleAndOffsetProvider() {
-            if (mExtras != null) {
-                return new CropViewScaleAndOffsetProvider() {
-                    @Override
-                    public void updateCropView(WallpaperCropActivity a, TileSource src) {
-                        a.mCropView.setScaleAndCenter(mExtras[0], mExtras[1], mExtras[2]);
-                    }
-                };
-            }
-            return null;
-        }
-
-        @Override
-        public void onSave(final WallpaperPickerActivity a) {
-            if (mExtras == null) {
-                super.onSave(a);
-            } else {
-                boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f;
-                a.cropImageAndSetWallpaper(Uri.fromFile(mFile), null, true, shouldFadeOutOnFinish);
-            }
-        }
-    }
-
-    public SavedWallpaperImages(Context context) {
-        // We used to store the saved images in the cache directory, but that meant they'd get
-        // deleted sometimes-- move them to the data directory
-        ImageDb.moveFromCacheDirectoryIfNecessary(context);
-        mDb = new ImageDb(context);
-        mContext = context;
-        mLayoutInflater = LayoutInflater.from(context);
-    }
-
-    public void loadThumbnailsAndImageIdList() {
-        mImages = new ArrayList<SavedWallpaperTile>();
-        SQLiteDatabase db = mDb.getReadableDatabase();
-        Cursor result = db.query(ImageDb.TABLE_NAME,
-                new String[] { ImageDb.COLUMN_ID,
-                    ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME,
-                    ImageDb.COLUMN_IMAGE_FILENAME,
-                    ImageDb.COLUMN_EXTRAS}, // cols to return
-                null, // select query
-                null, // args to select query
-                null,
-                null,
-                ImageDb.COLUMN_ID + " DESC",
-                null);
-
-        while (result.moveToNext()) {
-            String filename = result.getString(1);
-            File file = new File(mContext.getFilesDir(), filename);
-
-            Bitmap thumb = BitmapFactory.decodeFile(file.getAbsolutePath());
-            if (thumb != null) {
-
-                Float[] extras = null;
-                String extraStr = result.getString(3);
-                if (extraStr != null) {
-                    String[] parts = extraStr.split(",");
-                    extras = new Float[parts.length];
-                    for (int i = 0; i < parts.length; i++) {
-                        try {
-                            extras[i] = Float.parseFloat(parts[i]);
-                        } catch (Exception e) {
-                            extras = null;
-                            break;
-                        }
-                    }
-                }
-
-                mImages.add(new SavedWallpaperTile(result.getInt(0),
-                        new File(mContext.getFilesDir(), result.getString(2)),
-                        new BitmapDrawable(thumb), extras));
-            }
-        }
-        result.close();
-    }
-
-    public int getCount() {
-        return mImages.size();
-    }
-
-    public SavedWallpaperTile getItem(int position) {
-        return mImages.get(position);
-    }
-
-    public long getItemId(int position) {
-        return position;
-    }
-
-    public View getView(int position, View convertView, ViewGroup parent) {
-        Drawable thumbDrawable = mImages.get(position).mThumb;
-        if (thumbDrawable == null) {
-            Log.e(TAG, "Error decoding thumbnail for wallpaper #" + position);
-        }
-        return WallpaperPickerActivity.createImageTileView(
-                mLayoutInflater, convertView, parent, thumbDrawable);
-    }
-
-    private Pair<String, String> getImageFilenames(int id) {
-        SQLiteDatabase db = mDb.getReadableDatabase();
-        Cursor result = db.query(ImageDb.TABLE_NAME,
-                new String[] { ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME,
-                    ImageDb.COLUMN_IMAGE_FILENAME }, // cols to return
-                ImageDb.COLUMN_ID + " = ?", // select query
-                new String[] { Integer.toString(id) }, // args to select query
-                null,
-                null,
-                null,
-                null);
-        if (result.getCount() > 0) {
-            result.moveToFirst();
-            String thumbFilename = result.getString(0);
-            String imageFilename = result.getString(1);
-            result.close();
-            return new Pair<String, String>(thumbFilename, imageFilename);
-        } else {
-            return null;
-        }
-    }
-
-    public void deleteImage(int id) {
-        Pair<String, String> filenames = getImageFilenames(id);
-        File imageFile = new File(mContext.getFilesDir(), filenames.first);
-        imageFile.delete();
-        File thumbFile = new File(mContext.getFilesDir(), filenames.second);
-        thumbFile.delete();
-        SQLiteDatabase db = mDb.getWritableDatabase();
-        db.delete(ImageDb.TABLE_NAME,
-                ImageDb.COLUMN_ID + " = ?", // SELECT query
-                new String[] {
-                    Integer.toString(id) // args to SELECT query
-                });
-    }
-
-    public void writeImage(Bitmap thumbnail, byte[] imageBytes) {
-        try {
-            writeImage(thumbnail, new ByteArrayInputStream(imageBytes), null);
-        } catch (IOException e) {
-            Log.e(TAG, "Failed writing images to storage " + e);
-        }
-    }
-
-    public void writeImage(Bitmap thumbnail, Uri uri, Float[] extras) {
-        try {
-            writeImage(thumbnail, mContext.getContentResolver().openInputStream(uri), extras);
-        } catch (IOException e) {
-            Log.e(TAG, "Failed writing images to storage " + e);
-        }
-    }
-
-    private void writeImage(Bitmap thumbnail, InputStream in, Float[] extras) throws IOException {
-        File imageFile = File.createTempFile("wallpaper", "", mContext.getFilesDir());
-        FileOutputStream imageFileStream =
-                mContext.openFileOutput(imageFile.getName(), Context.MODE_PRIVATE);
-        byte[] buf = new byte[4096];    // 4k
-        int len;
-        while ((len = in.read(buf)) > 0) {
-            imageFileStream.write(buf, 0, len);
-        }
-        imageFileStream.close();
-        in.close();
-
-        File thumbFile = File.createTempFile("wallpaperthumb", "", mContext.getFilesDir());
-        FileOutputStream thumbFileStream =
-                mContext.openFileOutput(thumbFile.getName(), Context.MODE_PRIVATE);
-        thumbnail.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream);
-        thumbFileStream.close();
-
-        SQLiteDatabase db = mDb.getWritableDatabase();
-        ContentValues values = new ContentValues();
-        values.put(ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME, thumbFile.getName());
-        values.put(ImageDb.COLUMN_IMAGE_FILENAME, imageFile.getName());
-        if (extras != null) {
-            values.put(ImageDb.COLUMN_EXTRAS, TextUtils.join(",", extras));
-        }
-        db.insert(ImageDb.TABLE_NAME, null, values);
-    }
-
-    static class ImageDb extends SQLiteOpenHelper {
-        final static int DB_VERSION = 2;
-        final static String TABLE_NAME = "saved_wallpaper_images";
-        final static String COLUMN_ID = "id";
-        final static String COLUMN_IMAGE_THUMBNAIL_FILENAME = "image_thumbnail";
-        final static String COLUMN_IMAGE_FILENAME = "image";
-        final static String COLUMN_EXTRAS = "extras";
-
-        Context mContext;
-
-        public ImageDb(Context context) {
-            super(context, context.getDatabasePath(LauncherFiles.WALLPAPER_IMAGES_DB).getPath(),
-                    null, DB_VERSION);
-            // Store the context for later use
-            mContext = context;
-        }
-
-        public static void moveFromCacheDirectoryIfNecessary(Context context) {
-            // We used to store the saved images in the cache directory, but that meant they'd get
-            // deleted sometimes-- move them to the data directory
-            File oldSavedImagesFile = new File(context.getCacheDir(),
-                    LauncherFiles.WALLPAPER_IMAGES_DB);
-            File savedImagesFile = context.getDatabasePath(LauncherFiles.WALLPAPER_IMAGES_DB);
-            if (oldSavedImagesFile.exists()) {
-                oldSavedImagesFile.renameTo(savedImagesFile);
-            }
-        }
-        @Override
-        public void onCreate(SQLiteDatabase database) {
-            database.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
-                    COLUMN_ID + " INTEGER NOT NULL, " +
-                    COLUMN_IMAGE_THUMBNAIL_FILENAME + " TEXT NOT NULL, " +
-                    COLUMN_IMAGE_FILENAME + " TEXT NOT NULL, " +
-                    COLUMN_EXTRAS + " TEXT, " +
-                    "PRIMARY KEY (" + COLUMN_ID + " ASC) " +
-                    ");");
-        }
-
-        @Override
-        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            if (oldVersion == 1) {
-                // Add extras column
-                db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + COLUMN_EXTRAS + " TEXT;");
-            } else if (oldVersion != newVersion) {
-                // Delete all the records; they'll be repopulated as this is a cache
-                db.execSQL("DELETE FROM " + TABLE_NAME);
-                onCreate(db);
-            }
-        }
-    }
-}
diff --git a/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java b/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java
deleted file mode 100644
index 099bbda..0000000
--- a/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.ListAdapter;
-import android.widget.TextView;
-
-import com.android.launcher3.util.Thunk;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class ThirdPartyWallpaperPickerListAdapter extends BaseAdapter implements ListAdapter {
-    private final LayoutInflater mInflater;
-    private final PackageManager mPackageManager;
-    private final int mIconSize;
-
-    private List<ThirdPartyWallpaperTile> mThirdPartyWallpaperPickers =
-            new ArrayList<ThirdPartyWallpaperTile>();
-
-    public static class ThirdPartyWallpaperTile extends WallpaperPickerActivity.WallpaperTileInfo {
-        @Thunk ResolveInfo mResolveInfo;
-        public ThirdPartyWallpaperTile(ResolveInfo resolveInfo) {
-            mResolveInfo = resolveInfo;
-        }
-        @Override
-        public void onClick(WallpaperPickerActivity a) {
-            final ComponentName itemComponentName = new ComponentName(
-                    mResolveInfo.activityInfo.packageName, mResolveInfo.activityInfo.name);
-            Intent launchIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
-            launchIntent.setComponent(itemComponentName)
-            .putExtra(WallpaperPickerActivity.EXTRA_WALLPAPER_OFFSET,
-                    a.getWallpaperParallaxOffset());
-            a.startActivityForResultSafely(
-                    launchIntent, WallpaperPickerActivity.PICK_WALLPAPER_THIRD_PARTY_ACTIVITY);
-        }
-    }
-
-    public ThirdPartyWallpaperPickerListAdapter(Context context) {
-        mInflater = LayoutInflater.from(context);
-        mPackageManager = context.getPackageManager();
-        mIconSize = context.getResources().getDimensionPixelSize(R.dimen.wallpaperItemIconSize);
-        final PackageManager pm = mPackageManager;
-
-        final Intent pickWallpaperIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
-        final List<ResolveInfo> apps =
-                pm.queryIntentActivities(pickWallpaperIntent, 0);
-
-        // Get list of image picker intents
-        Intent pickImageIntent = new Intent(Intent.ACTION_GET_CONTENT);
-        pickImageIntent.setType("image/*");
-        final List<ResolveInfo> imagePickerActivities =
-                pm.queryIntentActivities(pickImageIntent, 0);
-        final ComponentName[] imageActivities = new ComponentName[imagePickerActivities.size()];
-        for (int i = 0; i < imagePickerActivities.size(); i++) {
-            ActivityInfo activityInfo = imagePickerActivities.get(i).activityInfo;
-            imageActivities[i] = new ComponentName(activityInfo.packageName, activityInfo.name);
-        }
-
-        outerLoop:
-        for (ResolveInfo info : apps) {
-            final ComponentName itemComponentName =
-                    new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
-            final String itemPackageName = itemComponentName.getPackageName();
-            // Exclude anything from our own package, and the old Launcher,
-            // and live wallpaper picker
-            if (itemPackageName.equals(context.getPackageName()) ||
-                    itemPackageName.equals("com.android.launcher") ||
-                    itemPackageName.equals("com.android.wallpaper.livepicker")) {
-                continue;
-            }
-            // Exclude any package that already responds to the image picker intent
-            for (ResolveInfo imagePickerActivityInfo : imagePickerActivities) {
-                if (itemPackageName.equals(
-                        imagePickerActivityInfo.activityInfo.packageName)) {
-                    continue outerLoop;
-                }
-            }
-            mThirdPartyWallpaperPickers.add(new ThirdPartyWallpaperTile(info));
-        }
-    }
-
-    public int getCount() {
-        return mThirdPartyWallpaperPickers.size();
-    }
-
-    public ThirdPartyWallpaperTile getItem(int position) {
-        return mThirdPartyWallpaperPickers.get(position);
-    }
-
-    public long getItemId(int position) {
-        return position;
-    }
-
-    public View getView(int position, View convertView, ViewGroup parent) {
-        View view;
-
-        if (convertView == null) {
-            view = mInflater.inflate(R.layout.wallpaper_picker_third_party_item, parent, false);
-        } else {
-            view = convertView;
-        }
-
-        ResolveInfo info = mThirdPartyWallpaperPickers.get(position).mResolveInfo;
-        TextView label = (TextView) view.findViewById(R.id.wallpaper_item_label);
-        label.setText(info.loadLabel(mPackageManager));
-        Drawable icon = info.loadIcon(mPackageManager);
-        icon.setBounds(new Rect(0, 0, mIconSize, mIconSize));
-        label.setCompoundDrawables(null, icon, null, null);
-        return view;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
deleted file mode 100644
index f6c53ec..0000000
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
+++ /dev/null
@@ -1,528 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.annotation.TargetApi;
-import android.app.ActionBar;
-import android.app.Activity;
-import android.app.WallpaperManager;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.RectF;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-import android.util.Log;
-import android.view.Display;
-import android.view.View;
-import android.widget.Toast;
-
-import com.android.gallery3d.common.BitmapCropTask;
-import com.android.gallery3d.common.BitmapUtils;
-import com.android.gallery3d.common.Utils;
-import com.android.launcher3.base.BaseActivity;
-import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.WallpaperUtils;
-import com.android.photos.BitmapRegionTileSource;
-import com.android.photos.BitmapRegionTileSource.BitmapSource;
-import com.android.photos.BitmapRegionTileSource.BitmapSource.InBitmapProvider;
-import com.android.photos.views.TiledImageRenderer.TileSource;
-
-import java.util.Collections;
-import java.util.Set;
-import java.util.WeakHashMap;
-
-public class WallpaperCropActivity extends BaseActivity implements Handler.Callback {
-    private static final String LOGTAG = "Launcher3.CropActivity";
-
-    protected static final String WALLPAPER_WIDTH_KEY = WallpaperUtils.WALLPAPER_WIDTH_KEY;
-    protected static final String WALLPAPER_HEIGHT_KEY = WallpaperUtils.WALLPAPER_HEIGHT_KEY;
-
-    /**
-     * The maximum bitmap size we allow to be returned through the intent.
-     * Intents have a maximum of 1MB in total size. However, the Bitmap seems to
-     * have some overhead to hit so that we go way below the limit here to make
-     * sure the intent stays below 1MB.We should consider just returning a byte
-     * array instead of a Bitmap instance to avoid overhead.
-     */
-    public static final int MAX_BMAP_IN_INTENT = 750000;
-    public static final float WALLPAPER_SCREENS_SPAN = WallpaperUtils.WALLPAPER_SCREENS_SPAN;
-
-    private static final int MSG_LOAD_IMAGE = 1;
-
-    protected CropView mCropView;
-    protected View mProgressView;
-    protected Uri mUri;
-    protected View mSetWallpaperButton;
-
-    private HandlerThread mLoaderThread;
-    private Handler mLoaderHandler;
-    @Thunk LoadRequest mCurrentLoadRequest;
-    private byte[] mTempStorageForDecoding = new byte[16 * 1024];
-    // A weak-set of reusable bitmaps
-    @Thunk Set<Bitmap> mReusableBitmaps =
-            Collections.newSetFromMap(new WeakHashMap<Bitmap, Boolean>());
-
-    private final DialogInterface.OnCancelListener mOnDialogCancelListener =
-            new DialogInterface.OnCancelListener() {
-                @Override
-                public void onCancel(DialogInterface dialog) {
-                    getActionBar().show();
-                    View wallpaperStrip = findViewById(R.id.wallpaper_strip);
-                    if (wallpaperStrip != null) {
-                        wallpaperStrip.setVisibility(View.VISIBLE);
-                    }
-                }
-            };
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mLoaderThread = new HandlerThread("wallpaper_loader");
-        mLoaderThread.start();
-        mLoaderHandler = new Handler(mLoaderThread.getLooper(), this);
-
-        init();
-        if (!enableRotation()) {
-            setRequestedOrientation(Configuration.ORIENTATION_PORTRAIT);
-        }
-    }
-
-    protected void init() {
-        setContentView(R.layout.wallpaper_cropper);
-
-        mCropView = (CropView) findViewById(R.id.cropView);
-        mProgressView = findViewById(R.id.loading);
-
-        Intent cropIntent = getIntent();
-        final Uri imageUri = cropIntent.getData();
-
-        if (imageUri == null) {
-            Log.e(LOGTAG, "No URI passed in intent, exiting WallpaperCropActivity");
-            finish();
-            return;
-        }
-
-        // Action bar
-        // Show the custom action bar view
-        final ActionBar actionBar = getActionBar();
-        actionBar.setCustomView(R.layout.actionbar_set_wallpaper);
-        actionBar.getCustomView().setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        actionBar.hide();
-                        boolean finishActivityWhenDone = true;
-                        // Never fade on finish because we return to the app that started us (e.g.
-                        // Photos), not the home screen.
-                        boolean shouldFadeOutOnFinish = false;
-                        cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone,
-                                shouldFadeOutOnFinish);
-                    }
-                });
-        mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
-
-        // Load image in background
-        final BitmapRegionTileSource.UriBitmapSource bitmapSource =
-                new BitmapRegionTileSource.UriBitmapSource(getContext(), imageUri);
-        mSetWallpaperButton.setEnabled(false);
-        Runnable onLoad = new Runnable() {
-            public void run() {
-                if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) {
-                    Toast.makeText(getContext(), R.string.wallpaper_load_fail,
-                            Toast.LENGTH_LONG).show();
-                    finish();
-                } else {
-                    mSetWallpaperButton.setEnabled(true);
-                }
-            }
-        };
-        setCropViewTileSource(bitmapSource, true, false, null, onLoad);
-    }
-
-    @Override
-    public void onDestroy() {
-        if (mCropView != null) {
-            mCropView.destroy();
-        }
-        if (mLoaderThread != null) {
-            mLoaderThread.quit();
-        }
-        super.onDestroy();
-    }
-
-    /**
-     * This is called on {@link #mLoaderThread}
-     */
-    @Override
-    public boolean handleMessage(Message msg) {
-        if (msg.what == MSG_LOAD_IMAGE) {
-            final LoadRequest req = (LoadRequest) msg.obj;
-            try {
-                req.src.loadInBackground(new InBitmapProvider() {
-
-                    @Override
-                    public Bitmap forPixelCount(int count) {
-                        Bitmap bitmapToReuse = null;
-                        // Find the smallest bitmap that satisfies the pixel count limit
-                        synchronized (mReusableBitmaps) {
-                            int currentBitmapSize = Integer.MAX_VALUE;
-                            for (Bitmap b : mReusableBitmaps) {
-                                int bitmapSize = b.getWidth() * b.getHeight();
-                                if ((bitmapSize >= count) && (bitmapSize < currentBitmapSize)) {
-                                    bitmapToReuse = b;
-                                    currentBitmapSize = bitmapSize;
-                                }
-                            }
-
-                            if (bitmapToReuse != null) {
-                                mReusableBitmaps.remove(bitmapToReuse);
-                            }
-                        }
-                        return bitmapToReuse;
-                    }
-                });
-            } catch (SecurityException securityException) {
-                if (isActivityDestroyed()) {
-                    // Temporarily granted permissions are revoked when the activity
-                    // finishes, potentially resulting in a SecurityException here.
-                    // Even though {@link #isDestroyed} might also return true in different
-                    // situations where the configuration changes, we are fine with
-                    // catching these cases here as well.
-                    return true;
-                } else {
-                    // otherwise it had a different cause and we throw it further
-                    throw securityException;
-                }
-            }
-
-            req.result = new BitmapRegionTileSource(getContext(), req.src, mTempStorageForDecoding);
-            runOnUiThread(new Runnable() {
-
-                @Override
-                public void run() {
-                    if (req == mCurrentLoadRequest) {
-                        onLoadRequestComplete(req,
-                                req.src.getLoadingState() == BitmapSource.State.LOADED);
-                    } else {
-                        addReusableBitmap(req.result);
-                    }
-                }
-            });
-            return true;
-        }
-        return false;
-    }
-
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    protected boolean isActivityDestroyed() {
-        return Utilities.ATLEAST_JB_MR1 && isDestroyed();
-    }
-
-    @Thunk void addReusableBitmap(TileSource src) {
-        synchronized (mReusableBitmaps) {
-            if (Utilities.ATLEAST_KITKAT && src instanceof BitmapRegionTileSource) {
-                Bitmap preview = ((BitmapRegionTileSource) src).getBitmap();
-                if (preview != null && preview.isMutable()) {
-                    mReusableBitmaps.add(preview);
-                }
-            }
-        }
-    }
-
-    public DialogInterface.OnCancelListener getOnDialogCancelListener() {
-        return mOnDialogCancelListener;
-    }
-
-    protected void onLoadRequestComplete(LoadRequest req, boolean success) {
-        mCurrentLoadRequest = null;
-        if (success) {
-            TileSource oldSrc = mCropView.getTileSource();
-            mCropView.setTileSource(req.result, null);
-            mCropView.setTouchEnabled(req.touchEnabled);
-            if (req.moveToLeft) {
-                mCropView.moveToLeft();
-            }
-            if (req.scaleAndOffsetProvider != null) {
-                req.scaleAndOffsetProvider.updateCropView(this, req.result);
-            }
-
-            // Free last image
-            if (oldSrc != null) {
-                // Call yield instead of recycle, as we only want to free GL resource.
-                // We can still reuse the bitmap for decoding any other image.
-                oldSrc.getPreview().yield();
-            }
-            addReusableBitmap(oldSrc);
-        }
-        if (req.postExecute != null) {
-            req.postExecute.run();
-        }
-        mProgressView.setVisibility(View.GONE);
-    }
-
-    public final void setCropViewTileSource(BitmapSource bitmapSource, boolean touchEnabled,
-            boolean moveToLeft, CropViewScaleAndOffsetProvider scaleProvider, Runnable postExecute) {
-        final LoadRequest req = new LoadRequest();
-        req.moveToLeft = moveToLeft;
-        req.src = bitmapSource;
-        req.touchEnabled = touchEnabled;
-        req.postExecute = postExecute;
-        req.scaleAndOffsetProvider = scaleProvider;
-        mCurrentLoadRequest = req;
-
-        // Remove any pending requests
-        mLoaderHandler.removeMessages(MSG_LOAD_IMAGE);
-        Message.obtain(mLoaderHandler, MSG_LOAD_IMAGE, req).sendToTarget();
-
-        // We don't want to show the spinner every time we load an image, because that would be
-        // annoying; instead, only start showing the spinner if loading the image has taken
-        // longer than 1 sec (ie 1000 ms)
-        mProgressView.postDelayed(new Runnable() {
-            public void run() {
-                if (mCurrentLoadRequest == req) {
-                    mProgressView.setVisibility(View.VISIBLE);
-                }
-            }
-        }, 1000);
-    }
-
-
-    public boolean enableRotation() {
-        return getResources().getBoolean(R.bool.allow_rotation);
-    }
-
-    protected void setWallpaper(Uri uri, final boolean finishActivityWhenDone,
-                final boolean shouldFadeOutOnFinish) {
-        int rotation = BitmapUtils.getRotationFromExif(getContext(), uri);
-        BitmapCropTask cropTask = new BitmapCropTask(
-                getContext(), uri, null, rotation, 0, 0, true, false, null);
-        final Point bounds = cropTask.getImageBounds();
-        BitmapCropTask.OnEndCropHandler onEndCrop = new BitmapCropTask.OnEndCropHandler() {
-            public void run(boolean cropSucceeded) {
-                updateWallpaperDimensions(bounds.x, bounds.y);
-                if (finishActivityWhenDone) {
-                    setResult(Activity.RESULT_OK);
-                    finish();
-                    if (cropSucceeded && shouldFadeOutOnFinish) {
-                        overridePendingTransition(0, R.anim.fade_out);
-                    }
-                }
-            }
-        };
-        cropTask.setOnEndRunnable(onEndCrop);
-        cropTask.setNoCrop(true);
-        NycWallpaperUtils.executeCropTaskAfterPrompt(this, cropTask, getOnDialogCancelListener());
-    }
-
-    protected void cropImageAndSetWallpaper(Resources res, int resId,
-                final boolean finishActivityWhenDone, final boolean shouldFadeOutOnFinish) {
-        // crop this image and scale it down to the default wallpaper size for
-        // this device
-        int rotation = BitmapUtils.getRotationFromExif(res, resId);
-        Point inSize = mCropView.getSourceDimensions();
-        Point outSize = WallpaperUtils.getDefaultWallpaperSize(getResources(), getWindowManager());
-        RectF crop = Utils.getMaxCropRect(
-                inSize.x, inSize.y, outSize.x, outSize.y, false);
-        BitmapCropTask.OnEndCropHandler onEndCrop = new BitmapCropTask.OnEndCropHandler() {
-            public void run(boolean cropSucceeded) {
-                // Passing 0, 0 will cause launcher to revert to using the
-                // default wallpaper size
-                updateWallpaperDimensions(0, 0);
-                if (finishActivityWhenDone) {
-                    setResult(Activity.RESULT_OK);
-                    finish();
-                    if (cropSucceeded && shouldFadeOutOnFinish) {
-                        overridePendingTransition(0, R.anim.fade_out);
-                    }
-                }
-            }
-        };
-        BitmapCropTask cropTask = new BitmapCropTask(getContext(), res, resId,
-                crop, rotation, outSize.x, outSize.y, true, false, onEndCrop);
-        NycWallpaperUtils.executeCropTaskAfterPrompt(this, cropTask, getOnDialogCancelListener());
-    }
-
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    protected void cropImageAndSetWallpaper(Uri uri,
-            BitmapCropTask.OnBitmapCroppedHandler onBitmapCroppedHandler,
-            final boolean finishActivityWhenDone, final boolean shouldFadeOutOnFinish) {
-        // Give some feedback so user knows something is happening.
-        mProgressView.setVisibility(View.VISIBLE);
-
-        boolean centerCrop = getResources().getBoolean(R.bool.center_crop);
-        // Get the crop
-        boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
-
-        Display d = getWindowManager().getDefaultDisplay();
-
-        Point displaySize = new Point();
-        d.getSize(displaySize);
-        boolean isPortrait = displaySize.x < displaySize.y;
-
-        Point defaultWallpaperSize = WallpaperUtils.getDefaultWallpaperSize(getResources(),
-                getWindowManager());
-        // Get the crop
-        RectF cropRect = mCropView.getCrop();
-
-        Point inSize = mCropView.getSourceDimensions();
-
-        int cropRotation = mCropView.getImageRotation();
-        float cropScale = mCropView.getWidth() / (float) cropRect.width();
-
-
-        Matrix rotateMatrix = new Matrix();
-        rotateMatrix.setRotate(cropRotation);
-        float[] rotatedInSize = new float[] { inSize.x, inSize.y };
-        rotateMatrix.mapPoints(rotatedInSize);
-        rotatedInSize[0] = Math.abs(rotatedInSize[0]);
-        rotatedInSize[1] = Math.abs(rotatedInSize[1]);
-
-
-        // due to rounding errors in the cropview renderer the edges can be slightly offset
-        // therefore we ensure that the boundaries are sanely defined
-        cropRect.left = Math.max(0, cropRect.left);
-        cropRect.right = Math.min(rotatedInSize[0], cropRect.right);
-        cropRect.top = Math.max(0, cropRect.top);
-        cropRect.bottom = Math.min(rotatedInSize[1], cropRect.bottom);
-
-        // ADJUST CROP WIDTH
-        // Extend the crop all the way to the right, for parallax
-        // (or all the way to the left, in RTL)
-        float extraSpace;
-        if (centerCrop) {
-            extraSpace = 2f * Math.min(rotatedInSize[0] - cropRect.right, cropRect.left);
-        } else {
-            extraSpace = ltr ? rotatedInSize[0] - cropRect.right : cropRect.left;
-        }
-        // Cap the amount of extra width
-        float maxExtraSpace = defaultWallpaperSize.x / cropScale - cropRect.width();
-        extraSpace = Math.min(extraSpace, maxExtraSpace);
-
-        if (centerCrop) {
-            cropRect.left -= extraSpace / 2f;
-            cropRect.right += extraSpace / 2f;
-        } else {
-            if (ltr) {
-                cropRect.right += extraSpace;
-            } else {
-                cropRect.left -= extraSpace;
-            }
-        }
-
-        // ADJUST CROP HEIGHT
-        if (isPortrait) {
-            cropRect.bottom = cropRect.top + defaultWallpaperSize.y / cropScale;
-        } else { // LANDSCAPE
-            float extraPortraitHeight =
-                    defaultWallpaperSize.y / cropScale - cropRect.height();
-            float expandHeight =
-                    Math.min(Math.min(rotatedInSize[1] - cropRect.bottom, cropRect.top),
-                            extraPortraitHeight / 2);
-            cropRect.top -= expandHeight;
-            cropRect.bottom += expandHeight;
-        }
-        final int outWidth = (int) Math.round(cropRect.width() * cropScale);
-        final int outHeight = (int) Math.round(cropRect.height() * cropScale);
-
-        BitmapCropTask.OnEndCropHandler onEndCrop = new BitmapCropTask.OnEndCropHandler() {
-            public void run(boolean cropSucceeded) {
-                updateWallpaperDimensions(outWidth, outHeight);
-                if (finishActivityWhenDone) {
-                    setResult(Activity.RESULT_OK);
-                    finish();
-                }
-                if (cropSucceeded && shouldFadeOutOnFinish) {
-                    overridePendingTransition(0, R.anim.fade_out);
-                }
-            }
-        };
-        BitmapCropTask cropTask = new BitmapCropTask(getContext(), uri,
-                cropRect, cropRotation, outWidth, outHeight, true, false, onEndCrop);
-        if (onBitmapCroppedHandler != null) {
-            cropTask.setOnBitmapCropped(onBitmapCroppedHandler);
-        }
-        NycWallpaperUtils.executeCropTaskAfterPrompt(this, cropTask, getOnDialogCancelListener());
-    }
-
-    protected void updateWallpaperDimensions(int width, int height) {
-        String spKey = LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY;
-        SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
-        SharedPreferences.Editor editor = sp.edit();
-        if (width != 0 && height != 0) {
-            editor.putInt(WALLPAPER_WIDTH_KEY, width);
-            editor.putInt(WALLPAPER_HEIGHT_KEY, height);
-        } else {
-            editor.remove(WALLPAPER_WIDTH_KEY);
-            editor.remove(WALLPAPER_HEIGHT_KEY);
-        }
-        editor.apply();
-        WallpaperUtils.suggestWallpaperDimension(getResources(),
-                sp, getWindowManager(), WallpaperManager.getInstance(getContext()), true);
-    }
-
-    static class LoadRequest {
-        BitmapSource src;
-        boolean touchEnabled;
-        boolean moveToLeft;
-        Runnable postExecute;
-        CropViewScaleAndOffsetProvider scaleAndOffsetProvider;
-
-        TileSource result;
-    }
-
-    public static class CropViewScaleAndOffsetProvider {
-        public float getScale(Point wallpaperSize, RectF crop) {
-            return 1f;
-        }
-
-        public float getParallaxOffset() {
-            return 0.5f;
-        }
-
-        public void updateCropView(WallpaperCropActivity a, TileSource src) {
-            Point wallpaperSize = WallpaperUtils.getDefaultWallpaperSize(
-                    a.getResources(), a.getWindowManager());
-            RectF crop = Utils.getMaxCropRect(src.getImageWidth(), src.getImageHeight(),
-                    wallpaperSize.x, wallpaperSize.y, false /* leftAligned */);
-
-            float scale = getScale(wallpaperSize, crop);
-            PointF center = a.mCropView.getCenter();
-
-            // Offsets wallpaper preview according to the state it will be displayed in upon
-            // returning home. Offset ranges from 0 to 1, where 0 is the leftmost parallax and
-            // 1 is the rightmost.
-            // Make sure the offset is in the correct range.
-            float offset = Math.max(0, Math.min(getParallaxOffset(), 1));
-            float screenWidth = a.mCropView.getWidth() / scale;
-            center.x = screenWidth / 2 + offset * (crop.width() - screenWidth) + crop.left;
-            a.mCropView.setScaleAndCenter(scale, center.x, center.y);
-        }
-    }
-}
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
deleted file mode 100644
index 40f0544..0000000
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ /dev/null
@@ -1,1295 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.Manifest;
-import android.animation.LayoutTransition;
-import android.annotation.TargetApi;
-import android.app.ActionBar;
-import android.app.Activity;
-import android.app.WallpaperManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.database.DataSetObserver;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapRegionDecoder;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Process;
-import android.provider.MediaStore;
-import android.support.annotation.NonNull;
-import android.util.Log;
-import android.util.Pair;
-import android.view.ActionMode;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLayoutChangeListener;
-import android.view.ViewGroup;
-import android.view.ViewPropertyAnimator;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-import android.view.WindowManager;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.ArrayAdapter;
-import android.widget.BaseAdapter;
-import android.widget.FrameLayout;
-import android.widget.HorizontalScrollView;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.Toast;
-
-import com.android.gallery3d.common.BitmapCropTask;
-import com.android.gallery3d.common.BitmapUtils;
-import com.android.gallery3d.common.Utils;
-import com.android.launcher3.util.Thunk;
-import com.android.photos.BitmapRegionTileSource;
-import com.android.photos.BitmapRegionTileSource.BitmapSource;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-
-public class WallpaperPickerActivity extends WallpaperCropActivity {
-    static final String TAG = "WallpaperPickerActivity";
-
-    public static final int IMAGE_PICK = 5;
-    public static final int PICK_WALLPAPER_THIRD_PARTY_ACTIVITY = 6;
-    /** An Intent extra used when opening the wallpaper picker from the workspace overlay. */
-    public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
-    private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES";
-    private static final String SELECTED_INDEX = "SELECTED_INDEX";
-    private static final int FLAG_POST_DELAY_MILLIS = 200;
-
-    @Thunk View mSelectedTile;
-    @Thunk boolean mIgnoreNextTap;
-    @Thunk OnClickListener mThumbnailOnClickListener;
-
-    @Thunk LinearLayout mWallpapersView;
-    @Thunk HorizontalScrollView mWallpaperScrollContainer;
-    @Thunk View mWallpaperStrip;
-
-    @Thunk ActionMode.Callback mActionModeCallback;
-    @Thunk ActionMode mActionMode;
-
-    @Thunk View.OnLongClickListener mLongClickListener;
-
-    ArrayList<Uri> mTempWallpaperTiles = new ArrayList<Uri>();
-    private SavedWallpaperImages mSavedImages;
-    @Thunk int mSelectedIndex = -1;
-    private float mWallpaperParallaxOffset;
-
-    public static abstract class WallpaperTileInfo {
-        protected View mView;
-        public Drawable mThumb;
-
-        public void setView(View v) {
-            mView = v;
-        }
-        public void onClick(WallpaperPickerActivity a) {}
-        public void onSave(WallpaperPickerActivity a) {}
-        public void onDelete(WallpaperPickerActivity a) {}
-        public boolean isSelectable() { return false; }
-        public boolean isNamelessWallpaper() { return false; }
-        public void onIndexUpdated(CharSequence label) {
-            if (isNamelessWallpaper()) {
-                mView.setContentDescription(label);
-            }
-        }
-    }
-
-    public static class PickImageInfo extends WallpaperTileInfo {
-        @Override
-        public void onClick(WallpaperPickerActivity a) {
-            Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
-            intent.setType("image/*");
-            a.startActivityForResultSafely(intent, IMAGE_PICK);
-        }
-    }
-
-    public static class UriWallpaperInfo extends WallpaperTileInfo {
-        private Uri mUri;
-        public UriWallpaperInfo(Uri uri) {
-            mUri = uri;
-        }
-        @Override
-        public void onClick(final WallpaperPickerActivity a) {
-            a.setWallpaperButtonEnabled(false);
-            final BitmapRegionTileSource.UriBitmapSource bitmapSource =
-                    new BitmapRegionTileSource.UriBitmapSource(a.getContext(), mUri);
-            a.setCropViewTileSource(bitmapSource, true, false, null, new Runnable() {
-
-                @Override
-                public void run() {
-                    if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
-                        a.selectTile(mView);
-                        a.setWallpaperButtonEnabled(true);
-                    } else {
-                        ViewGroup parent = (ViewGroup) mView.getParent();
-                        if (parent != null) {
-                            parent.removeView(mView);
-                            Toast.makeText(a.getContext(), R.string.image_load_fail,
-                                    Toast.LENGTH_SHORT).show();
-                        }
-                    }
-                }
-            });
-        }
-        @Override
-        public void onSave(final WallpaperPickerActivity a) {
-            boolean finishActivityWhenDone = true;
-            BitmapCropTask.OnBitmapCroppedHandler h = new BitmapCropTask.OnBitmapCroppedHandler() {
-                @Override
-                public void onBitmapCropped(byte[] imageBytes, Rect hint) {
-                    Bitmap thumb = null;
-                    Point thumbSize = getDefaultThumbnailSize(a.getResources());
-                    if (imageBytes != null) {
-                        // rotation is set to 0 since imageBytes has already been correctly rotated
-                        thumb = createThumbnail(
-                                thumbSize, null, null, imageBytes, null, 0, 0, true);
-                        a.getSavedImages().writeImage(thumb, imageBytes);
-                    } else {
-                        try {
-                            // Generate thumb
-                            Point size = getDefaultThumbnailSize(a.getResources());
-                            Rect finalCropped = new Rect();
-                            Utils.getMaxCropRect(hint.width(), hint.height(), size.x, size.y, false)
-                                    .roundOut(finalCropped);
-                            finalCropped.offset(hint.left, hint.top);
-
-                            InputStream in = a.getContentResolver().openInputStream(mUri);
-                            BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(in, true);
-
-                            BitmapFactory.Options options = new BitmapFactory.Options();
-                            options.inSampleSize = finalCropped.width() / size.x;
-                            thumb = decoder.decodeRegion(finalCropped, options);
-                            decoder.recycle();
-                            Utils.closeSilently(in);
-                            if (thumb != null) {
-                                thumb = Bitmap.createScaledBitmap(thumb, size.x, size.y, true);
-                            }
-                        } catch (IOException e) { }
-                        PointF center = a.mCropView.getCenter();
-
-                        Float[] extras = new Float[] {
-                                a.mCropView.getScale(),
-                                center.x,
-                                center.y
-                        };
-                        a.getSavedImages().writeImage(thumb, mUri, extras);
-                    }
-                }
-            };
-            boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f;
-            a.cropImageAndSetWallpaper(mUri, h, finishActivityWhenDone, shouldFadeOutOnFinish);
-        }
-        @Override
-        public boolean isSelectable() {
-            return true;
-        }
-        @Override
-        public boolean isNamelessWallpaper() {
-            return true;
-        }
-    }
-
-    public static class FileWallpaperInfo extends WallpaperTileInfo {
-        protected File mFile;
-
-        public FileWallpaperInfo(File target, Drawable thumb) {
-            mFile = target;
-            mThumb = thumb;
-        }
-        @Override
-        public void onClick(final WallpaperPickerActivity a) {
-            a.setWallpaperButtonEnabled(false);
-            final BitmapRegionTileSource.UriBitmapSource bitmapSource =
-                    new BitmapRegionTileSource.UriBitmapSource(a.getContext(), Uri.fromFile(mFile));
-            a.setCropViewTileSource(bitmapSource, false, true, getCropViewScaleAndOffsetProvider(),
-                    new Runnable() {
-
-                @Override
-                public void run() {
-                    if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
-                        a.setWallpaperButtonEnabled(true);
-                    }
-                }
-            });
-        }
-
-        protected CropViewScaleAndOffsetProvider getCropViewScaleAndOffsetProvider() {
-            return null;
-        }
-
-        @Override
-        public void onSave(WallpaperPickerActivity a) {
-            boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f;
-            a.setWallpaper(Uri.fromFile(mFile), true, shouldFadeOutOnFinish);
-        }
-        @Override
-        public boolean isSelectable() {
-            return true;
-        }
-        @Override
-        public boolean isNamelessWallpaper() {
-            return true;
-        }
-    }
-
-    public static class ResourceWallpaperInfo extends WallpaperTileInfo {
-        private Resources mResources;
-        private int mResId;
-
-        public ResourceWallpaperInfo(Resources res, int resId, Drawable thumb) {
-            mResources = res;
-            mResId = resId;
-            mThumb = thumb;
-        }
-        @Override
-        public void onClick(final WallpaperPickerActivity a) {
-            a.setWallpaperButtonEnabled(false);
-            final BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
-                    new BitmapRegionTileSource.ResourceBitmapSource(mResources, mResId);
-            a.setCropViewTileSource(bitmapSource, false, false, new CropViewScaleAndOffsetProvider() {
-
-                @Override
-                public float getScale(Point wallpaperSize, RectF crop) {
-                    return wallpaperSize.x /crop.width();
-                }
-
-                @Override
-                public float getParallaxOffset() {
-                    return a.getWallpaperParallaxOffset();
-                }
-            }, new Runnable() {
-
-                @Override
-                public void run() {
-                    if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
-                        a.setWallpaperButtonEnabled(true);
-                    }
-                }
-            });
-        }
-        @Override
-        public void onSave(WallpaperPickerActivity a) {
-            boolean finishActivityWhenDone = true;
-            boolean shouldFadeOutOnFinish = true;
-            a.cropImageAndSetWallpaper(mResources, mResId, finishActivityWhenDone,
-                    shouldFadeOutOnFinish);
-        }
-        @Override
-        public boolean isSelectable() {
-            return true;
-        }
-        @Override
-        public boolean isNamelessWallpaper() {
-            return true;
-        }
-    }
-
-    @TargetApi(Build.VERSION_CODES.KITKAT)
-    public static class DefaultWallpaperInfo extends WallpaperTileInfo {
-        public DefaultWallpaperInfo(Drawable thumb) {
-            mThumb = thumb;
-        }
-        @Override
-        public void onClick(WallpaperPickerActivity a) {
-            CropView c = a.getCropView();
-            Drawable defaultWallpaper = WallpaperManager.getInstance(a.getContext())
-                    .getBuiltInDrawable(c.getWidth(), c.getHeight(), false, 0.5f, 0.5f);
-            if (defaultWallpaper == null) {
-                Log.w(TAG, "Null default wallpaper encountered.");
-                c.setTileSource(null, null);
-                return;
-            }
-
-            LoadRequest req = new LoadRequest();
-            req.moveToLeft = false;
-            req.touchEnabled = false;
-            req.scaleAndOffsetProvider = new CropViewScaleAndOffsetProvider();
-            req.result = new DrawableTileSource(a.getContext(),
-                    defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE);
-            a.onLoadRequestComplete(req, true);
-        }
-        @Override
-        public void onSave(final WallpaperPickerActivity a) {
-            if (!Utilities.isNycOrAbove()) {
-                try {
-                    WallpaperManager.getInstance(a.getContext()).clear();
-                    a.setResult(Activity.RESULT_OK);
-                } catch (IOException e) {
-                    Log.e(TAG, "Setting wallpaper to default threw exception", e);
-                } catch (SecurityException e) {
-                    Log.w(TAG, "Setting wallpaper to default threw exception", e);
-                    // In this case, clearing worked; the exception was thrown afterwards.
-                    a.setResult(Activity.RESULT_OK);
-                }
-                a.finish();
-            } else {
-                BitmapCropTask.OnEndCropHandler onEndCropHandler
-                        = new BitmapCropTask.OnEndCropHandler() {
-                    @Override
-                    public void run(boolean cropSucceeded) {
-                        if (cropSucceeded) {
-                            a.setResult(Activity.RESULT_OK);
-                        }
-                        a.finish();
-                    }
-                };
-                BitmapCropTask setWallpaperTask = getDefaultWallpaperCropTask(a, onEndCropHandler);
-
-                NycWallpaperUtils.executeCropTaskAfterPrompt(a, setWallpaperTask,
-                        a.getOnDialogCancelListener());
-            }
-        }
-
-        @NonNull
-        private BitmapCropTask getDefaultWallpaperCropTask(final WallpaperPickerActivity a,
-                final BitmapCropTask.OnEndCropHandler onEndCropHandler) {
-            return new BitmapCropTask(a, null, null, -1, -1, -1,
-                    true, false, onEndCropHandler) {
-                @Override
-                protected Boolean doInBackground(Integer... params) {
-                    int whichWallpaper = params[0];
-                    boolean succeeded = true;
-                    try {
-                        if (whichWallpaper == NycWallpaperUtils.FLAG_SET_LOCK) {
-                            Bitmap defaultWallpaper = ((BitmapDrawable) WallpaperManager
-                                    .getInstance(a.getApplicationContext()).getBuiltInDrawable())
-                                    .getBitmap();
-                            ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
-                            if (defaultWallpaper.compress(Bitmap.CompressFormat.PNG, 100,
-                                    tmpOut)) {
-                                byte[] outByteArray = tmpOut.toByteArray();
-                                NycWallpaperUtils.setStream(a.getApplicationContext(),
-                                        new ByteArrayInputStream(outByteArray), null, true,
-                                        NycWallpaperUtils.FLAG_SET_LOCK);
-                            }
-                        } else {
-                            NycWallpaperUtils.clear(a, whichWallpaper);
-                        }
-                    } catch (IOException e) {
-                        Log.e(TAG, "Setting wallpaper to default threw exception", e);
-                        succeeded = false;
-                    } catch (SecurityException e) {
-                        Log.w(TAG, "Setting wallpaper to default threw exception", e);
-                        // In this case, clearing worked; the exception was thrown afterwards.
-                        succeeded = true;
-                    }
-                    return succeeded;
-                }
-            };
-        }
-
-        @Override
-        public boolean isSelectable() {
-            return true;
-        }
-        @Override
-        public boolean isNamelessWallpaper() {
-            return true;
-        }
-    }
-
-    /**
-     * shows the system wallpaper behind the window and hides the {@link
-     * #mCropView} if visible
-     * @param visible should the system wallpaper be shown
-     */
-    protected void setSystemWallpaperVisiblity(final boolean visible) {
-        // hide our own wallpaper preview if necessary
-        if(!visible) {
-            mCropView.setVisibility(View.VISIBLE);
-        } else {
-            changeWallpaperFlags(visible);
-        }
-        // the change of the flag must be delayed in order to avoid flickering,
-        // a simple post / double post does not suffice here
-        mCropView.postDelayed(new Runnable() {
-            @Override
-            public void run() {
-                if(!visible) {
-                    changeWallpaperFlags(visible);
-                } else {
-                    mCropView.setVisibility(View.INVISIBLE);
-                }
-            }
-        }, FLAG_POST_DELAY_MILLIS);
-    }
-
-    @Thunk void changeWallpaperFlags(boolean visible) {
-        int desiredWallpaperFlag = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
-        int currentWallpaperFlag = getWindow().getAttributes().flags
-                & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-        if (desiredWallpaperFlag != currentWallpaperFlag) {
-            getWindow().setFlags(desiredWallpaperFlag,
-                    WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
-        }
-    }
-
-    @Override
-    protected void onLoadRequestComplete(LoadRequest req, boolean success) {
-        super.onLoadRequestComplete(req, success);
-        if (success) {
-            setSystemWallpaperVisiblity(false);
-        }
-    }
-
-    // called by onCreate; this is subclassed to overwrite WallpaperCropActivity
-    protected void init() {
-        setContentView(R.layout.wallpaper_picker);
-
-        mCropView = (CropView) findViewById(R.id.cropView);
-        mCropView.setVisibility(View.INVISIBLE);
-
-        mProgressView = findViewById(R.id.loading);
-        mWallpaperScrollContainer = (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container);
-        mWallpaperStrip = findViewById(R.id.wallpaper_strip);
-        mCropView.setTouchCallback(new CropView.TouchCallback() {
-            ViewPropertyAnimator mAnim;
-            @Override
-            public void onTouchDown() {
-                if (mAnim != null) {
-                    mAnim.cancel();
-                }
-                if (mWallpaperStrip.getAlpha() == 1f) {
-                    mIgnoreNextTap = true;
-                }
-                mAnim = mWallpaperStrip.animate();
-                mAnim.alpha(0f)
-                    .setDuration(150)
-                    .withEndAction(new Runnable() {
-                        public void run() {
-                            mWallpaperStrip.setVisibility(View.INVISIBLE);
-                        }
-                    });
-                mAnim.setInterpolator(new AccelerateInterpolator(0.75f));
-                mAnim.start();
-            }
-            @Override
-            public void onTouchUp() {
-                mIgnoreNextTap = false;
-            }
-            @Override
-            public void onTap() {
-                boolean ignoreTap = mIgnoreNextTap;
-                mIgnoreNextTap = false;
-                if (!ignoreTap) {
-                    if (mAnim != null) {
-                        mAnim.cancel();
-                    }
-                    mWallpaperStrip.setVisibility(View.VISIBLE);
-                    mAnim = mWallpaperStrip.animate();
-                    mAnim.alpha(1f)
-                         .setDuration(150)
-                         .setInterpolator(new DecelerateInterpolator(0.75f));
-                    mAnim.start();
-                }
-            }
-        });
-
-        mThumbnailOnClickListener = new OnClickListener() {
-            public void onClick(View v) {
-                if (mActionMode != null) {
-                    // When CAB is up, clicking toggles the item instead
-                    if (v.isLongClickable()) {
-                        mLongClickListener.onLongClick(v);
-                    }
-                    return;
-                }
-                WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
-                if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
-                    selectTile(v);
-                    setWallpaperButtonEnabled(true);
-                }
-                info.onClick(WallpaperPickerActivity.this);
-            }
-        };
-        mLongClickListener = new View.OnLongClickListener() {
-            // Called when the user long-clicks on someView
-            public boolean onLongClick(View view) {
-                CheckableFrameLayout c = (CheckableFrameLayout) view;
-                c.toggle();
-
-                if (mActionMode != null) {
-                    mActionMode.invalidate();
-                } else {
-                    // Start the CAB using the ActionMode.Callback defined below
-                    mActionMode = startActionMode(mActionModeCallback);
-                    int childCount = mWallpapersView.getChildCount();
-                    for (int i = 0; i < childCount; i++) {
-                        mWallpapersView.getChildAt(i).setSelected(false);
-                    }
-                }
-                return true;
-            }
-        };
-
-        mWallpaperParallaxOffset = getIntent().getFloatExtra(EXTRA_WALLPAPER_OFFSET, 0);
-
-        // Populate the built-in wallpapers
-        ArrayList<WallpaperTileInfo> wallpapers = findBundledWallpapers();
-        mWallpapersView = (LinearLayout) findViewById(R.id.wallpaper_list);
-        SimpleWallpapersAdapter ia = new SimpleWallpapersAdapter(getContext(), wallpapers);
-        populateWallpapersFromAdapter(mWallpapersView, ia, false);
-
-        // Populate the saved wallpapers
-        mSavedImages = new SavedWallpaperImages(getContext());
-        mSavedImages.loadThumbnailsAndImageIdList();
-        populateWallpapersFromAdapter(mWallpapersView, mSavedImages, true);
-
-        // Populate the live wallpapers
-        final LinearLayout liveWallpapersView =
-                (LinearLayout) findViewById(R.id.live_wallpaper_list);
-        final LiveWallpaperListAdapter a = new LiveWallpaperListAdapter(getContext());
-        a.registerDataSetObserver(new DataSetObserver() {
-            public void onChanged() {
-                liveWallpapersView.removeAllViews();
-                populateWallpapersFromAdapter(liveWallpapersView, a, false);
-                initializeScrollForRtl();
-                updateTileIndices();
-            }
-        });
-
-        // Populate the third-party wallpaper pickers
-        final LinearLayout thirdPartyWallpapersView =
-                (LinearLayout) findViewById(R.id.third_party_wallpaper_list);
-        final ThirdPartyWallpaperPickerListAdapter ta =
-                new ThirdPartyWallpaperPickerListAdapter(getContext());
-        populateWallpapersFromAdapter(thirdPartyWallpapersView, ta, false);
-
-        // Add a tile for the Gallery
-        LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list);
-        FrameLayout pickImageTile = (FrameLayout) getLayoutInflater().
-                inflate(R.layout.wallpaper_picker_image_picker_item, masterWallpaperList, false);
-        masterWallpaperList.addView(pickImageTile, 0);
-
-        // Make its background the last photo taken on external storage
-        Bitmap lastPhoto = getThumbnailOfLastPhoto();
-        if (lastPhoto != null) {
-            ImageView galleryThumbnailBg =
-                    (ImageView) pickImageTile.findViewById(R.id.wallpaper_image);
-            galleryThumbnailBg.setImageBitmap(lastPhoto);
-            int colorOverlay = getResources().getColor(R.color.wallpaper_picker_translucent_gray);
-            galleryThumbnailBg.setColorFilter(colorOverlay, PorterDuff.Mode.SRC_ATOP);
-        }
-
-        PickImageInfo pickImageInfo = new PickImageInfo();
-        pickImageTile.setTag(pickImageInfo);
-        pickImageInfo.setView(pickImageTile);
-        pickImageTile.setOnClickListener(mThumbnailOnClickListener);
-
-        // Select the first item; wait for a layout pass so that we initialize the dimensions of
-        // cropView or the defaultWallpaperView first
-        mCropView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
-            @Override
-            public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                if ((right - left) > 0 && (bottom - top) > 0) {
-                    if (mSelectedIndex >= 0 && mSelectedIndex < mWallpapersView.getChildCount()) {
-                        mThumbnailOnClickListener.onClick(
-                                mWallpapersView.getChildAt(mSelectedIndex));
-                        setSystemWallpaperVisiblity(false);
-                    }
-                    v.removeOnLayoutChangeListener(this);
-                }
-            }
-        });
-
-        updateTileIndices();
-
-        // Update the scroll for RTL
-        initializeScrollForRtl();
-
-        // Create smooth layout transitions for when items are deleted
-        final LayoutTransition transitioner = new LayoutTransition();
-        transitioner.setDuration(200);
-        transitioner.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
-        transitioner.setAnimator(LayoutTransition.DISAPPEARING, null);
-        mWallpapersView.setLayoutTransition(transitioner);
-
-        // Action bar
-        // Show the custom action bar view
-        final ActionBar actionBar = getActionBar();
-        actionBar.setCustomView(R.layout.actionbar_set_wallpaper);
-        actionBar.getCustomView().setOnClickListener(
-                new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        // Ensure that a tile is selected and loaded.
-                        if (mSelectedTile != null && mCropView.getTileSource() != null) {
-                            // Prevent user from selecting any new tile.
-                            mWallpaperStrip.setVisibility(View.GONE);
-                            actionBar.hide();
-
-                            WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag();
-                            info.onSave(WallpaperPickerActivity.this);
-                        } else {
-                            // This case shouldn't be possible, since "Set wallpaper" is disabled
-                            // until user clicks on a title.
-                            Log.w(TAG, "\"Set wallpaper\" was clicked when no tile was selected");
-                        }
-                    }
-                });
-        mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
-
-        // CAB for deleting items
-        mActionModeCallback = new ActionMode.Callback() {
-            // Called when the action mode is created; startActionMode() was called
-            @Override
-            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-                // Inflate a menu resource providing context menu items
-                MenuInflater inflater = mode.getMenuInflater();
-                inflater.inflate(R.menu.cab_delete_wallpapers, menu);
-                return true;
-            }
-
-            private int numCheckedItems() {
-                int childCount = mWallpapersView.getChildCount();
-                int numCheckedItems = 0;
-                for (int i = 0; i < childCount; i++) {
-                    CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
-                    if (c.isChecked()) {
-                        numCheckedItems++;
-                    }
-                }
-                return numCheckedItems;
-            }
-
-            // Called each time the action mode is shown. Always called after onCreateActionMode,
-            // but may be called multiple times if the mode is invalidated.
-            @Override
-            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-                int numCheckedItems = numCheckedItems();
-                if (numCheckedItems == 0) {
-                    mode.finish();
-                    return true;
-                } else {
-                    mode.setTitle(getResources().getQuantityString(
-                            R.plurals.number_of_items_selected, numCheckedItems, numCheckedItems));
-                    return true;
-                }
-            }
-
-            // Called when the user selects a contextual menu item
-            @Override
-            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-                int itemId = item.getItemId();
-                if (itemId == R.id.menu_delete) {
-                    int childCount = mWallpapersView.getChildCount();
-                    ArrayList<View> viewsToRemove = new ArrayList<View>();
-                    boolean selectedTileRemoved = false;
-                    for (int i = 0; i < childCount; i++) {
-                        CheckableFrameLayout c =
-                                (CheckableFrameLayout) mWallpapersView.getChildAt(i);
-                        if (c.isChecked()) {
-                            WallpaperTileInfo info = (WallpaperTileInfo) c.getTag();
-                            info.onDelete(WallpaperPickerActivity.this);
-                            viewsToRemove.add(c);
-                            if (i == mSelectedIndex) {
-                                selectedTileRemoved = true;
-                            }
-                        }
-                    }
-                    for (View v : viewsToRemove) {
-                        mWallpapersView.removeView(v);
-                    }
-                    if (selectedTileRemoved) {
-                        mSelectedIndex = -1;
-                        mSelectedTile = null;
-                        setSystemWallpaperVisiblity(true);
-                    }
-                    updateTileIndices();
-                    mode.finish(); // Action picked, so close the CAB
-                    return true;
-                } else {
-                    return false;
-                }
-            }
-
-            // Called when the user exits the action mode
-            @Override
-            public void onDestroyActionMode(ActionMode mode) {
-                int childCount = mWallpapersView.getChildCount();
-                for (int i = 0; i < childCount; i++) {
-                    CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
-                    c.setChecked(false);
-                }
-                if (mSelectedTile != null) {
-                    mSelectedTile.setSelected(true);
-                }
-                mActionMode = null;
-            }
-        };
-    }
-
-    public void setWallpaperButtonEnabled(boolean enabled) {
-        mSetWallpaperButton.setEnabled(enabled);
-    }
-
-    public float getWallpaperParallaxOffset() {
-        return mWallpaperParallaxOffset;
-    }
-
-    @Thunk void selectTile(View v) {
-        if (mSelectedTile != null) {
-            mSelectedTile.setSelected(false);
-            mSelectedTile = null;
-        }
-        mSelectedTile = v;
-        v.setSelected(true);
-        mSelectedIndex = mWallpapersView.indexOfChild(v);
-        // TODO: Remove this once the accessibility framework and
-        // services have better support for selection state.
-        v.announceForAccessibility(
-                getContext().getString(R.string.announce_selection, v.getContentDescription()));
-    }
-
-    @Thunk void initializeScrollForRtl() {
-        if (Utilities.isRtl(getResources())) {
-            final ViewTreeObserver observer = mWallpaperScrollContainer.getViewTreeObserver();
-            observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
-                public void onGlobalLayout() {
-                    LinearLayout masterWallpaperList =
-                            (LinearLayout) findViewById(R.id.master_wallpaper_list);
-                    mWallpaperScrollContainer.scrollTo(masterWallpaperList.getWidth(), 0);
-                    mWallpaperScrollContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
-                }
-            });
-        }
-    }
-
-    protected Bitmap getThumbnailOfLastPhoto() {
-        boolean canReadExternalStorage = getActivity().checkPermission(
-                Manifest.permission.READ_EXTERNAL_STORAGE, Process.myPid(), Process.myUid()) ==
-                PackageManager.PERMISSION_GRANTED;
-
-        if (!canReadExternalStorage) {
-            // MediaStore.Images.Media.EXTERNAL_CONTENT_URI requires
-            // the READ_EXTERNAL_STORAGE permission
-            return null;
-        }
-
-        Cursor cursor = MediaStore.Images.Media.query(getContext().getContentResolver(),
-                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
-                new String[] { MediaStore.Images.ImageColumns._ID,
-                    MediaStore.Images.ImageColumns.DATE_TAKEN},
-                null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC LIMIT 1");
-
-        Bitmap thumb = null;
-        if (cursor != null) {
-            if (cursor.moveToNext()) {
-                int id = cursor.getInt(0);
-                thumb = MediaStore.Images.Thumbnails.getThumbnail(getContext().getContentResolver(),
-                        id, MediaStore.Images.Thumbnails.MINI_KIND, null);
-            }
-            cursor.close();
-        }
-        return thumb;
-    }
-
-    public void onStop() {
-        super.onStop();
-        mWallpaperStrip = findViewById(R.id.wallpaper_strip);
-        if (mWallpaperStrip.getAlpha() < 1f) {
-            mWallpaperStrip.setAlpha(1f);
-            mWallpaperStrip.setVisibility(View.VISIBLE);
-        }
-    }
-
-    public void onSaveInstanceState(Bundle outState) {
-        outState.putParcelableArrayList(TEMP_WALLPAPER_TILES, mTempWallpaperTiles);
-        outState.putInt(SELECTED_INDEX, mSelectedIndex);
-    }
-
-    protected void onRestoreInstanceState(Bundle savedInstanceState) {
-        ArrayList<Uri> uris = savedInstanceState.getParcelableArrayList(TEMP_WALLPAPER_TILES);
-        for (Uri uri : uris) {
-            addTemporaryWallpaperTile(uri, true);
-        }
-        mSelectedIndex = savedInstanceState.getInt(SELECTED_INDEX, -1);
-    }
-
-    @Thunk void populateWallpapersFromAdapter(ViewGroup parent, BaseAdapter adapter,
-            boolean addLongPressHandler) {
-        for (int i = 0; i < adapter.getCount(); i++) {
-            FrameLayout thumbnail = (FrameLayout) adapter.getView(i, null, parent);
-            parent.addView(thumbnail, i);
-            WallpaperTileInfo info = (WallpaperTileInfo) adapter.getItem(i);
-            thumbnail.setTag(info);
-            info.setView(thumbnail);
-            if (addLongPressHandler) {
-                addLongPressHandler(thumbnail);
-            }
-            thumbnail.setOnClickListener(mThumbnailOnClickListener);
-        }
-    }
-
-    @Thunk void updateTileIndices() {
-        LinearLayout masterWallpaperList = (LinearLayout) findViewById(R.id.master_wallpaper_list);
-        final int childCount = masterWallpaperList.getChildCount();
-        final Resources res = getResources();
-
-        // Do two passes; the first pass gets the total number of tiles
-        int numTiles = 0;
-        for (int passNum = 0; passNum < 2; passNum++) {
-            int tileIndex = 0;
-            for (int i = 0; i < childCount; i++) {
-                View child = masterWallpaperList.getChildAt(i);
-                LinearLayout subList;
-
-                int subListStart;
-                int subListEnd;
-                if (child.getTag() instanceof WallpaperTileInfo) {
-                    subList = masterWallpaperList;
-                    subListStart = i;
-                    subListEnd = i + 1;
-                } else { // if (child instanceof LinearLayout) {
-                    subList = (LinearLayout) child;
-                    subListStart = 0;
-                    subListEnd = subList.getChildCount();
-                }
-
-                for (int j = subListStart; j < subListEnd; j++) {
-                    WallpaperTileInfo info = (WallpaperTileInfo) subList.getChildAt(j).getTag();
-                    if (info.isNamelessWallpaper()) {
-                        if (passNum == 0) {
-                            numTiles++;
-                        } else {
-                            CharSequence label = res.getString(
-                                    R.string.wallpaper_accessibility_name, ++tileIndex, numTiles);
-                            info.onIndexUpdated(label);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    @Thunk static Point getDefaultThumbnailSize(Resources res) {
-        return new Point(res.getDimensionPixelSize(R.dimen.wallpaperThumbnailWidth),
-                res.getDimensionPixelSize(R.dimen.wallpaperThumbnailHeight));
-
-    }
-
-    @Thunk static Bitmap createThumbnail(Point size, Context context, Uri uri, byte[] imageBytes,
-            Resources res, int resId, int rotation, boolean leftAligned) {
-        int width = size.x;
-        int height = size.y;
-
-        BitmapCropTask cropTask;
-        if (uri != null) {
-            cropTask = new BitmapCropTask(
-                    context, uri, null, rotation, width, height, false, true, null);
-        } else if (imageBytes != null) {
-            cropTask = new BitmapCropTask(
-                    imageBytes, null, rotation, width, height, false, true, null);
-        }  else {
-            cropTask = new BitmapCropTask(
-                    context, res, resId, null, rotation, width, height, false, true, null);
-        }
-        Point bounds = cropTask.getImageBounds();
-        if (bounds == null || bounds.x == 0 || bounds.y == 0) {
-            return null;
-        }
-
-        Matrix rotateMatrix = new Matrix();
-        rotateMatrix.setRotate(rotation);
-        float[] rotatedBounds = new float[] { bounds.x, bounds.y };
-        rotateMatrix.mapPoints(rotatedBounds);
-        rotatedBounds[0] = Math.abs(rotatedBounds[0]);
-        rotatedBounds[1] = Math.abs(rotatedBounds[1]);
-
-        RectF cropRect = Utils.getMaxCropRect(
-                (int) rotatedBounds[0], (int) rotatedBounds[1], width, height, leftAligned);
-        cropTask.setCropBounds(cropRect);
-
-        if (cropTask.cropBitmap(NycWallpaperUtils.FLAG_SET_SYSTEM)) {
-            return cropTask.getCroppedBitmap();
-        } else {
-            return null;
-        }
-    }
-
-    private void addTemporaryWallpaperTile(final Uri uri, boolean fromRestore) {
-        // Add a tile for the image picked from Gallery, reusing the existing tile if there is one.
-        FrameLayout existingImageThumbnail = null;
-        int indexOfExistingTile = 0;
-        for (; indexOfExistingTile < mWallpapersView.getChildCount(); indexOfExistingTile++) {
-            FrameLayout thumbnail = (FrameLayout) mWallpapersView.getChildAt(indexOfExistingTile);
-            Object tag = thumbnail.getTag();
-            if (tag instanceof UriWallpaperInfo && ((UriWallpaperInfo) tag).mUri.equals(uri)) {
-                existingImageThumbnail = thumbnail;
-                break;
-            }
-        }
-        final FrameLayout pickedImageThumbnail;
-        if (existingImageThumbnail != null) {
-            pickedImageThumbnail = existingImageThumbnail;
-            // Always move the existing wallpaper to the front so user can see it without scrolling.
-            mWallpapersView.removeViewAt(indexOfExistingTile);
-            mWallpapersView.addView(existingImageThumbnail, 0);
-        } else {
-            // This is the first time this temporary wallpaper has been added
-            pickedImageThumbnail = (FrameLayout) getLayoutInflater()
-                    .inflate(R.layout.wallpaper_picker_item, mWallpapersView, false);
-            pickedImageThumbnail.setVisibility(View.GONE);
-            mWallpapersView.addView(pickedImageThumbnail, 0);
-            mTempWallpaperTiles.add(uri);
-        }
-
-        // Load the thumbnail
-        final ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
-        final Point defaultSize = getDefaultThumbnailSize(this.getResources());
-        final Context context = getContext();
-        new AsyncTask<Void, Bitmap, Bitmap>() {
-            protected Bitmap doInBackground(Void...args) {
-                try {
-                    int rotation = BitmapUtils.getRotationFromExif(context, uri);
-                    return createThumbnail(defaultSize, context, uri, null, null, 0, rotation,
-                            false);
-                } catch (SecurityException securityException) {
-                    if (isActivityDestroyed()) {
-                        // Temporarily granted permissions are revoked when the activity
-                        // finishes, potentially resulting in a SecurityException here.
-                        // Even though {@link #isDestroyed} might also return true in different
-                        // situations where the configuration changes, we are fine with
-                        // catching these cases here as well.
-                        cancel(false);
-                    } else {
-                        // otherwise it had a different cause and we throw it further
-                        throw securityException;
-                    }
-                    return null;
-                }
-            }
-            protected void onPostExecute(Bitmap thumb) {
-                if (!isCancelled() && thumb != null) {
-                    image.setImageBitmap(thumb);
-                    Drawable thumbDrawable = image.getDrawable();
-                    thumbDrawable.setDither(true);
-                    pickedImageThumbnail.setVisibility(View.VISIBLE);
-                } else {
-                    Log.e(TAG, "Error loading thumbnail for uri=" + uri);
-                }
-            }
-        }.execute();
-
-        UriWallpaperInfo info = new UriWallpaperInfo(uri);
-        pickedImageThumbnail.setTag(info);
-        info.setView(pickedImageThumbnail);
-        addLongPressHandler(pickedImageThumbnail);
-        updateTileIndices();
-        pickedImageThumbnail.setOnClickListener(mThumbnailOnClickListener);
-        if (!fromRestore) {
-            mThumbnailOnClickListener.onClick(pickedImageThumbnail);
-        }
-    }
-
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == IMAGE_PICK && resultCode == Activity.RESULT_OK) {
-            if (data != null && data.getData() != null) {
-                Uri uri = data.getData();
-                addTemporaryWallpaperTile(uri, false);
-            }
-        } else if (requestCode == PICK_WALLPAPER_THIRD_PARTY_ACTIVITY
-                && resultCode == Activity.RESULT_OK) {
-            // Something was set on the third-party activity.
-            setResult(Activity.RESULT_OK);
-            finish();
-        }
-    }
-
-    private void addLongPressHandler(View v) {
-        v.setOnLongClickListener(mLongClickListener);
-
-        // Enable stylus button to also trigger long click.
-        final StylusEventHelper stylusEventHelper = new StylusEventHelper(v);
-        v.setOnTouchListener(new View.OnTouchListener() {
-            @Override
-            public boolean onTouch(View view, MotionEvent event) {
-                return stylusEventHelper.checkAndPerformStylusEvent(event);
-            }
-        });
-    }
-
-    private ArrayList<WallpaperTileInfo> findBundledWallpapers() {
-        final PackageManager pm = getContext().getPackageManager();
-        final ArrayList<WallpaperTileInfo> bundled = new ArrayList<WallpaperTileInfo>(24);
-
-        Partner partner = Partner.get(pm);
-        if (partner != null) {
-            final Resources partnerRes = partner.getResources();
-            final int resId = partnerRes.getIdentifier(Partner.RES_WALLPAPERS, "array",
-                    partner.getPackageName());
-            if (resId != 0) {
-                addWallpapers(bundled, partnerRes, partner.getPackageName(), resId);
-            }
-
-            // Add system wallpapers
-            File systemDir = partner.getWallpaperDirectory();
-            if (systemDir != null && systemDir.isDirectory()) {
-                for (File file : systemDir.listFiles()) {
-                    if (!file.isFile()) {
-                        continue;
-                    }
-                    String name = file.getName();
-                    int dotPos = name.lastIndexOf('.');
-                    String extension = "";
-                    if (dotPos >= -1) {
-                        extension = name.substring(dotPos);
-                        name = name.substring(0, dotPos);
-                    }
-
-                    if (name.endsWith("_small")) {
-                        // it is a thumbnail
-                        continue;
-                    }
-
-                    File thumbnail = new File(systemDir, name + "_small" + extension);
-                    Bitmap thumb = BitmapFactory.decodeFile(thumbnail.getAbsolutePath());
-                    if (thumb != null) {
-                        bundled.add(new FileWallpaperInfo(file, new BitmapDrawable(thumb)));
-                    }
-                }
-            }
-        }
-
-        Pair<ApplicationInfo, Integer> r = getWallpaperArrayResourceId();
-        if (r != null) {
-            try {
-                Resources wallpaperRes = getContext().getPackageManager()
-                        .getResourcesForApplication(r.first);
-                addWallpapers(bundled, wallpaperRes, r.first.packageName, r.second);
-            } catch (PackageManager.NameNotFoundException e) {
-            }
-        }
-
-        if (partner == null || !partner.hideDefaultWallpaper()) {
-            // Add an entry for the default wallpaper (stored in system resources)
-            WallpaperTileInfo defaultWallpaperInfo = Utilities.ATLEAST_KITKAT
-                    ? getDefaultWallpaper() : getPreKKDefaultWallpaperInfo();
-            if (defaultWallpaperInfo != null) {
-                bundled.add(0, defaultWallpaperInfo);
-            }
-        }
-        return bundled;
-    }
-
-    private boolean writeImageToFileAsJpeg(File f, Bitmap b) {
-        try {
-            f.createNewFile();
-            FileOutputStream thumbFileStream =
-                    getContext().openFileOutput(f.getName(), Context.MODE_PRIVATE);
-            b.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream);
-            thumbFileStream.close();
-            return true;
-        } catch (IOException e) {
-            Log.e(TAG, "Error while writing bitmap to file " + e);
-            f.delete();
-        }
-        return false;
-    }
-
-    private File getDefaultThumbFile() {
-        return new File(getContext().getFilesDir(), Build.VERSION.SDK_INT
-                + "_" + LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL);
-    }
-
-    private boolean saveDefaultWallpaperThumb(Bitmap b) {
-        // Delete old thumbnails.
-        new File(getContext().getFilesDir(), LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL_OLD).delete();
-        new File(getContext().getFilesDir(), LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL).delete();
-
-        for (int i = Build.VERSION_CODES.JELLY_BEAN; i < Build.VERSION.SDK_INT; i++) {
-            new File(getContext().getFilesDir(), i + "_"
-                    + LauncherFiles.DEFAULT_WALLPAPER_THUMBNAIL).delete();
-        }
-        return writeImageToFileAsJpeg(getDefaultThumbFile(), b);
-    }
-
-    private ResourceWallpaperInfo getPreKKDefaultWallpaperInfo() {
-        Resources sysRes = Resources.getSystem();
-        int resId = sysRes.getIdentifier("default_wallpaper", "drawable", "android");
-
-        File defaultThumbFile = getDefaultThumbFile();
-        Bitmap thumb = null;
-        boolean defaultWallpaperExists = false;
-        if (defaultThumbFile.exists()) {
-            thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath());
-            defaultWallpaperExists = true;
-        } else {
-            Resources res = getResources();
-            Point defaultThumbSize = getDefaultThumbnailSize(res);
-            int rotation = BitmapUtils.getRotationFromExif(res, resId);
-            thumb = createThumbnail(
-                    defaultThumbSize, getContext(), null, null, sysRes, resId, rotation, false);
-            if (thumb != null) {
-                defaultWallpaperExists = saveDefaultWallpaperThumb(thumb);
-            }
-        }
-        if (defaultWallpaperExists) {
-            return new ResourceWallpaperInfo(sysRes, resId, new BitmapDrawable(thumb));
-        }
-        return null;
-    }
-
-    @TargetApi(Build.VERSION_CODES.KITKAT)
-    private DefaultWallpaperInfo getDefaultWallpaper() {
-        File defaultThumbFile = getDefaultThumbFile();
-        Bitmap thumb = null;
-        boolean defaultWallpaperExists = false;
-        if (defaultThumbFile.exists()) {
-            thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath());
-            defaultWallpaperExists = true;
-        } else {
-            Resources res = getResources();
-            Point defaultThumbSize = getDefaultThumbnailSize(res);
-            Drawable wallpaperDrawable = WallpaperManager.getInstance(getContext()).getBuiltInDrawable(
-                    defaultThumbSize.x, defaultThumbSize.y, true, 0.5f, 0.5f);
-            if (wallpaperDrawable != null) {
-                thumb = Bitmap.createBitmap(
-                        defaultThumbSize.x, defaultThumbSize.y, Bitmap.Config.ARGB_8888);
-                Canvas c = new Canvas(thumb);
-                wallpaperDrawable.setBounds(0, 0, defaultThumbSize.x, defaultThumbSize.y);
-                wallpaperDrawable.draw(c);
-                c.setBitmap(null);
-            }
-            if (thumb != null) {
-                defaultWallpaperExists = saveDefaultWallpaperThumb(thumb);
-            }
-        }
-        if (defaultWallpaperExists) {
-            return new DefaultWallpaperInfo(new BitmapDrawable(thumb));
-        }
-        return null;
-    }
-
-    public Pair<ApplicationInfo, Integer> getWallpaperArrayResourceId() {
-        // Context.getPackageName() may return the "original" package name,
-        // com.android.launcher3; Resources needs the real package name,
-        // com.android.launcher3. So we ask Resources for what it thinks the
-        // package name should be.
-        final String packageName = getResources().getResourcePackageName(R.array.wallpapers);
-        try {
-            ApplicationInfo info = getContext().getPackageManager().getApplicationInfo(packageName, 0);
-            return new Pair<ApplicationInfo, Integer>(info, R.array.wallpapers);
-        } catch (PackageManager.NameNotFoundException e) {
-            return null;
-        }
-    }
-
-    private void addWallpapers(ArrayList<WallpaperTileInfo> known, Resources res,
-            String packageName, int listResId) {
-        final String[] extras = res.getStringArray(listResId);
-        for (String extra : extras) {
-            int resId = res.getIdentifier(extra, "drawable", packageName);
-            if (resId != 0) {
-                final int thumbRes = res.getIdentifier(extra + "_small", "drawable", packageName);
-
-                if (thumbRes != 0) {
-                    ResourceWallpaperInfo wallpaperInfo =
-                            new ResourceWallpaperInfo(res, resId, res.getDrawable(thumbRes));
-                    known.add(wallpaperInfo);
-                    // Log.d(TAG, "add: [" + packageName + "]: " + extra + " (" + res + ")");
-                }
-            } else {
-                Log.e(TAG, "Couldn't find wallpaper " + extra);
-            }
-        }
-    }
-
-    public CropView getCropView() {
-        return mCropView;
-    }
-
-    public SavedWallpaperImages getSavedImages() {
-        return mSavedImages;
-    }
-
-    private static class SimpleWallpapersAdapter extends ArrayAdapter<WallpaperTileInfo> {
-        private final LayoutInflater mLayoutInflater;
-
-        SimpleWallpapersAdapter(Context context, ArrayList<WallpaperTileInfo> wallpapers) {
-            super(context, R.layout.wallpaper_picker_item, wallpapers);
-            mLayoutInflater = LayoutInflater.from(context);
-        }
-
-        public View getView(int position, View convertView, ViewGroup parent) {
-            Drawable thumb = getItem(position).mThumb;
-            if (thumb == null) {
-                Log.e(TAG, "Error decoding thumbnail for wallpaper #" + position);
-            }
-            return createImageTileView(mLayoutInflater, convertView, parent, thumb);
-        }
-    }
-
-    public static View createImageTileView(LayoutInflater layoutInflater,
-            View convertView, ViewGroup parent, Drawable thumb) {
-        View view;
-
-        if (convertView == null) {
-            view = layoutInflater.inflate(R.layout.wallpaper_picker_item, parent, false);
-        } else {
-            view = convertView;
-        }
-
-        ImageView image = (ImageView) view.findViewById(R.id.wallpaper_image);
-
-        if (thumb != null) {
-            image.setImageDrawable(thumb);
-            thumb.setDither(true);
-        }
-
-        return view;
-    }
-
-    public void startActivityForResultSafely(Intent intent, int requestCode) {
-        Utilities.startActivityForResultSafely(getActivity(), intent, requestCode);
-    }
-
-    @Override
-    public boolean enableRotation() {
-        return true;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/launcher3/base/BaseActivity.java b/WallpaperPicker/src/com/android/launcher3/base/BaseActivity.java
deleted file mode 100644
index f854118..0000000
--- a/WallpaperPicker/src/com/android/launcher3/base/BaseActivity.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.android.launcher3.base;
-
-import android.app.Activity;
-import android.content.Context;
-
-/**
- * A wrapper over {@link Activity} which allows to override some methods.
- * The base implementation can change from an Activity to a Fragment (or any other custom
- * implementation), Callers should not assume that the base class extends Context, instead use
- * either {@link #getContext} or {@link #getActivity}
- */
-public class BaseActivity extends Activity {
-
-    public Context getContext() {
-        return this;
-    }
-
-    public Activity getActivity() {
-        return this;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
deleted file mode 100644
index 6baac6a..0000000
--- a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
+++ /dev/null
@@ -1,481 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.photos;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapRegionDecoder;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.opengl.GLUtils;
-import android.os.Build;
-import android.util.Log;
-
-import com.android.gallery3d.common.BitmapUtils;
-import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.exif.ExifInterface;
-import com.android.gallery3d.glrenderer.BasicTexture;
-import com.android.gallery3d.glrenderer.BitmapTexture;
-import com.android.photos.views.TiledImageRenderer;
-
-import java.io.BufferedInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-
-interface SimpleBitmapRegionDecoder {
-    int getWidth();
-    int getHeight();
-    Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options);
-}
-
-class SimpleBitmapRegionDecoderWrapper implements SimpleBitmapRegionDecoder {
-    BitmapRegionDecoder mDecoder;
-    private SimpleBitmapRegionDecoderWrapper(BitmapRegionDecoder decoder) {
-        mDecoder = decoder;
-    }
-    public static SimpleBitmapRegionDecoderWrapper newInstance(
-            String pathName, boolean isShareable) {
-        try {
-            BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(pathName, isShareable);
-            if (d != null) {
-                return new SimpleBitmapRegionDecoderWrapper(d);
-            }
-        } catch (IOException e) {
-            Log.w("BitmapRegionTileSource", "getting decoder failed for path " + pathName, e);
-            return null;
-        }
-        return null;
-    }
-    public static SimpleBitmapRegionDecoderWrapper newInstance(
-            InputStream is, boolean isShareable) {
-        try {
-            BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(is, isShareable);
-            if (d != null) {
-                return new SimpleBitmapRegionDecoderWrapper(d);
-            }
-        } catch (IOException e) {
-            Log.w("BitmapRegionTileSource", "getting decoder failed", e);
-            return null;
-        }
-        return null;
-    }
-    public int getWidth() {
-        return mDecoder.getWidth();
-    }
-    public int getHeight() {
-        return mDecoder.getHeight();
-    }
-    public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) {
-        return mDecoder.decodeRegion(wantRegion, options);
-    }
-}
-
-class DumbBitmapRegionDecoder implements SimpleBitmapRegionDecoder {
-    Bitmap mBuffer;
-    Canvas mTempCanvas;
-    Paint mTempPaint;
-    private DumbBitmapRegionDecoder(Bitmap b) {
-        mBuffer = b;
-    }
-    public static DumbBitmapRegionDecoder newInstance(String pathName) {
-        Bitmap b = BitmapFactory.decodeFile(pathName);
-        if (b != null) {
-            return new DumbBitmapRegionDecoder(b);
-        }
-        return null;
-    }
-    public static DumbBitmapRegionDecoder newInstance(InputStream is) {
-        Bitmap b = BitmapFactory.decodeStream(is);
-        if (b != null) {
-            return new DumbBitmapRegionDecoder(b);
-        }
-        return null;
-    }
-    public int getWidth() {
-        return mBuffer.getWidth();
-    }
-    public int getHeight() {
-        return mBuffer.getHeight();
-    }
-    public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) {
-        if (mTempCanvas == null) {
-            mTempCanvas = new Canvas();
-            mTempPaint = new Paint();
-            mTempPaint.setFilterBitmap(true);
-        }
-        int sampleSize = Math.max(options.inSampleSize, 1);
-        Bitmap newBitmap = Bitmap.createBitmap(
-                wantRegion.width() / sampleSize,
-                wantRegion.height() / sampleSize,
-                Bitmap.Config.ARGB_8888);
-        mTempCanvas.setBitmap(newBitmap);
-        mTempCanvas.save();
-        mTempCanvas.scale(1f / sampleSize, 1f / sampleSize);
-        mTempCanvas.drawBitmap(mBuffer, -wantRegion.left, -wantRegion.top, mTempPaint);
-        mTempCanvas.restore();
-        mTempCanvas.setBitmap(null);
-        return newBitmap;
-    }
-}
-
-/**
- * A {@link com.android.photos.views.TiledImageRenderer.TileSource} using
- * {@link BitmapRegionDecoder} to wrap a local file
- */
-@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
-public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
-
-    private static final String TAG = "BitmapRegionTileSource";
-
-    private static final int GL_SIZE_LIMIT = 2048;
-    // This must be no larger than half the size of the GL_SIZE_LIMIT
-    // due to decodePreview being allowed to be up to 2x the size of the target
-    private static final int MAX_PREVIEW_SIZE = GL_SIZE_LIMIT / 2;
-
-    public static abstract class BitmapSource {
-        private SimpleBitmapRegionDecoder mDecoder;
-        private Bitmap mPreview;
-        private int mRotation;
-        public enum State { NOT_LOADED, LOADED, ERROR_LOADING };
-        private State mState = State.NOT_LOADED;
-
-        /** Returns whether loading was successful. */
-        public boolean loadInBackground(InBitmapProvider bitmapProvider) {
-            ExifInterface ei = new ExifInterface();
-            if (readExif(ei)) {
-                Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
-                if (ori != null) {
-                    mRotation = ExifInterface.getRotationForOrientationValue(ori.shortValue());
-                }
-            }
-            mDecoder = loadBitmapRegionDecoder();
-            if (mDecoder == null) {
-                mState = State.ERROR_LOADING;
-                return false;
-            } else {
-                int width = mDecoder.getWidth();
-                int height = mDecoder.getHeight();
-
-                BitmapFactory.Options opts = new BitmapFactory.Options();
-                opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
-                opts.inPreferQualityOverSpeed = true;
-
-                float scale = (float) MAX_PREVIEW_SIZE / Math.max(width, height);
-                opts.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
-                opts.inJustDecodeBounds = false;
-                opts.inMutable = true;
-
-                if (bitmapProvider != null) {
-                    int expectedPixles = (width / opts.inSampleSize) * (height / opts.inSampleSize);
-                    Bitmap reusableBitmap = bitmapProvider.forPixelCount(expectedPixles);
-                    if (reusableBitmap != null) {
-                        // Try loading with reusable bitmap
-                        opts.inBitmap = reusableBitmap;
-                        try {
-                            mPreview = loadPreviewBitmap(opts);
-                        } catch (IllegalArgumentException e) {
-                            Log.d(TAG, "Unable to reuse bitmap", e);
-                            opts.inBitmap = null;
-                            mPreview = null;
-                        }
-                    }
-                }
-                if (mPreview == null) {
-                    mPreview = loadPreviewBitmap(opts);
-                }
-                if (mPreview == null) {
-                    mState = State.ERROR_LOADING;
-                    return false;
-                }
-
-                // Verify that the bitmap can be used on GL surface
-                try {
-                    GLUtils.getInternalFormat(mPreview);
-                    GLUtils.getType(mPreview);
-                    mState = State.LOADED;
-                } catch (IllegalArgumentException e) {
-                    Log.d(TAG, "Image cannot be rendered on a GL surface", e);
-                    mState = State.ERROR_LOADING;
-                }
-                return mState == State.LOADED;
-            }
-        }
-
-        public State getLoadingState() {
-            return mState;
-        }
-
-        public SimpleBitmapRegionDecoder getBitmapRegionDecoder() {
-            return mDecoder;
-        }
-
-        public Bitmap getPreviewBitmap() {
-            return mPreview;
-        }
-
-        public int getRotation() {
-            return mRotation;
-        }
-
-        public abstract boolean readExif(ExifInterface ei);
-        public abstract SimpleBitmapRegionDecoder loadBitmapRegionDecoder();
-        public abstract Bitmap loadPreviewBitmap(BitmapFactory.Options options);
-
-        public interface InBitmapProvider {
-            Bitmap forPixelCount(int count);
-        }
-    }
-
-    public static class FilePathBitmapSource extends BitmapSource {
-        private String mPath;
-        public FilePathBitmapSource(String path) {
-            mPath = path;
-        }
-        @Override
-        public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() {
-            SimpleBitmapRegionDecoder d;
-            d = SimpleBitmapRegionDecoderWrapper.newInstance(mPath, true);
-            if (d == null) {
-                d = DumbBitmapRegionDecoder.newInstance(mPath);
-            }
-            return d;
-        }
-        @Override
-        public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
-            return BitmapFactory.decodeFile(mPath, options);
-        }
-        @Override
-        public boolean readExif(ExifInterface ei) {
-            try {
-                ei.readExif(mPath);
-                return true;
-            } catch (NullPointerException e) {
-                Log.w("BitmapRegionTileSource", "reading exif failed", e);
-                return false;
-            } catch (IOException e) {
-                Log.w("BitmapRegionTileSource", "getting decoder failed", e);
-                return false;
-            }
-        }
-    }
-
-    public static class UriBitmapSource extends BitmapSource {
-        private Context mContext;
-        private Uri mUri;
-        public UriBitmapSource(Context context, Uri uri) {
-            mContext = context;
-            mUri = uri;
-        }
-        private InputStream regenerateInputStream() throws FileNotFoundException {
-            InputStream is = mContext.getContentResolver().openInputStream(mUri);
-            return new BufferedInputStream(is);
-        }
-        @Override
-        public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() {
-            try {
-                InputStream is = regenerateInputStream();
-                SimpleBitmapRegionDecoder regionDecoder =
-                        SimpleBitmapRegionDecoderWrapper.newInstance(is, false);
-                Utils.closeSilently(is);
-                if (regionDecoder == null) {
-                    is = regenerateInputStream();
-                    regionDecoder = DumbBitmapRegionDecoder.newInstance(is);
-                    Utils.closeSilently(is);
-                }
-                return regionDecoder;
-            } catch (FileNotFoundException e) {
-                Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
-                return null;
-            }
-        }
-        @Override
-        public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
-            try {
-                InputStream is = regenerateInputStream();
-                Bitmap b = BitmapFactory.decodeStream(is, null, options);
-                Utils.closeSilently(is);
-                return b;
-            } catch (FileNotFoundException | OutOfMemoryError e) {
-                Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
-                return null;
-            }
-        }
-        @Override
-        public boolean readExif(ExifInterface ei) {
-            InputStream is = null;
-            try {
-                is = regenerateInputStream();
-                ei.readExif(is);
-                Utils.closeSilently(is);
-                return true;
-            } catch (FileNotFoundException e) {
-                Log.d("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
-                return false;
-            } catch (IOException e) {
-                Log.d("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
-                return false;
-            } catch (NullPointerException e) {
-                Log.d("BitmapRegionTileSource", "Failed to read EXIF for URI " + mUri, e);
-                return false;
-            } finally {
-                Utils.closeSilently(is);
-            }
-        }
-    }
-
-    public static class ResourceBitmapSource extends BitmapSource {
-        private Resources mRes;
-        private int mResId;
-        public ResourceBitmapSource(Resources res, int resId) {
-            mRes = res;
-            mResId = resId;
-        }
-        private InputStream regenerateInputStream() {
-            InputStream is = mRes.openRawResource(mResId);
-            return new BufferedInputStream(is);
-        }
-        @Override
-        public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() {
-            InputStream is = regenerateInputStream();
-            SimpleBitmapRegionDecoder regionDecoder =
-                    SimpleBitmapRegionDecoderWrapper.newInstance(is, false);
-            Utils.closeSilently(is);
-            if (regionDecoder == null) {
-                is = regenerateInputStream();
-                regionDecoder = DumbBitmapRegionDecoder.newInstance(is);
-                Utils.closeSilently(is);
-            }
-            return regionDecoder;
-        }
-        @Override
-        public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
-            return BitmapFactory.decodeResource(mRes, mResId, options);
-        }
-        @Override
-        public boolean readExif(ExifInterface ei) {
-            try {
-                InputStream is = regenerateInputStream();
-                ei.readExif(is);
-                Utils.closeSilently(is);
-                return true;
-            } catch (IOException e) {
-                Log.e("BitmapRegionTileSource", "Error reading resource", e);
-                return false;
-            }
-        }
-    }
-
-    SimpleBitmapRegionDecoder mDecoder;
-    int mWidth;
-    int mHeight;
-    int mTileSize;
-    private BasicTexture mPreview;
-    private final int mRotation;
-
-    // For use only by getTile
-    private Rect mWantRegion = new Rect();
-    private BitmapFactory.Options mOptions;
-
-    public BitmapRegionTileSource(Context context, BitmapSource source, byte[] tempStorage) {
-        mTileSize = TiledImageRenderer.suggestedTileSize(context);
-        mRotation = source.getRotation();
-        mDecoder = source.getBitmapRegionDecoder();
-        if (mDecoder != null) {
-            mWidth = mDecoder.getWidth();
-            mHeight = mDecoder.getHeight();
-            mOptions = new BitmapFactory.Options();
-            mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
-            mOptions.inPreferQualityOverSpeed = true;
-            mOptions.inTempStorage = tempStorage;
-
-            Bitmap preview = source.getPreviewBitmap();
-            if (preview != null &&
-                    preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) {
-                    mPreview = new BitmapTexture(preview);
-            } else {
-                Log.w(TAG, String.format(
-                        "Failed to create preview of apropriate size! "
-                        + " in: %dx%d, out: %dx%d",
-                        mWidth, mHeight,
-                        preview == null ? -1 : preview.getWidth(),
-                        preview == null ? -1 : preview.getHeight()));
-            }
-        }
-    }
-
-    public Bitmap getBitmap() {
-        return mPreview instanceof BitmapTexture ? ((BitmapTexture) mPreview).getBitmap() : null;
-    }
-
-    @Override
-    public int getTileSize() {
-        return mTileSize;
-    }
-
-    @Override
-    public int getImageWidth() {
-        return mWidth;
-    }
-
-    @Override
-    public int getImageHeight() {
-        return mHeight;
-    }
-
-    @Override
-    public BasicTexture getPreview() {
-        return mPreview;
-    }
-
-    @Override
-    public int getRotation() {
-        return mRotation;
-    }
-
-    @Override
-    public Bitmap getTile(int level, int x, int y, Bitmap bitmap) {
-        int tileSize = getTileSize();
-        int t = tileSize << level;
-        mWantRegion.set(x, y, x + t, y + t);
-
-        if (bitmap == null) {
-            bitmap = Bitmap.createBitmap(tileSize, tileSize, Bitmap.Config.ARGB_8888);
-        }
-
-        mOptions.inSampleSize = (1 << level);
-        mOptions.inBitmap = bitmap;
-
-        try {
-            bitmap = mDecoder.decodeRegion(mWantRegion, mOptions);
-        } finally {
-            if (mOptions.inBitmap != bitmap && mOptions.inBitmap != null) {
-                mOptions.inBitmap = null;
-            }
-        }
-
-        if (bitmap == null) {
-            Log.w("BitmapRegionTileSource", "fail in decoding region");
-        }
-        return bitmap;
-    }
-}
diff --git a/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java b/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java
deleted file mode 100644
index e57ce70..0000000
--- a/WallpaperPicker/src/com/android/photos/views/TiledImageRenderer.java
+++ /dev/null
@@ -1,826 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.photos.views;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.support.v4.util.Pools.Pool;
-import android.support.v4.util.Pools.SynchronizedPool;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.LongSparseArray;
-import android.view.View;
-import android.view.WindowManager;
-
-import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.glrenderer.BasicTexture;
-import com.android.gallery3d.glrenderer.GLCanvas;
-import com.android.gallery3d.glrenderer.UploadedTexture;
-import com.android.launcher3.util.Thunk;
-
-/**
- * Handles laying out, decoding, and drawing of tiles in GL
- */
-public class TiledImageRenderer {
-    public static final int SIZE_UNKNOWN = -1;
-
-    private static final String TAG = "TiledImageRenderer";
-    private static final int UPLOAD_LIMIT = 1;
-
-    /*
-     *  This is the tile state in the CPU side.
-     *  Life of a Tile:
-     *      ACTIVATED (initial state)
-     *              --> IN_QUEUE - by queueForDecode()
-     *              --> RECYCLED - by recycleTile()
-     *      IN_QUEUE --> DECODING - by decodeTile()
-     *               --> RECYCLED - by recycleTile)
-     *      DECODING --> RECYCLING - by recycleTile()
-     *               --> DECODED  - by decodeTile()
-     *               --> DECODE_FAIL - by decodeTile()
-     *      RECYCLING --> RECYCLED - by decodeTile()
-     *      DECODED --> ACTIVATED - (after the decoded bitmap is uploaded)
-     *      DECODED --> RECYCLED - by recycleTile()
-     *      DECODE_FAIL -> RECYCLED - by recycleTile()
-     *      RECYCLED --> ACTIVATED - by obtainTile()
-     */
-    private static final int STATE_ACTIVATED = 0x01;
-    private static final int STATE_IN_QUEUE = 0x02;
-    private static final int STATE_DECODING = 0x04;
-    private static final int STATE_DECODED = 0x08;
-    private static final int STATE_DECODE_FAIL = 0x10;
-    private static final int STATE_RECYCLING = 0x20;
-    private static final int STATE_RECYCLED = 0x40;
-
-    @Thunk static Pool<Bitmap> sTilePool = new SynchronizedPool<Bitmap>(64);
-
-    // TILE_SIZE must be 2^N
-    @Thunk int mTileSize;
-
-    @Thunk TileSource mModel;
-    private BasicTexture mPreview;
-    protected int mLevelCount;  // cache the value of mScaledBitmaps.length
-
-    // The mLevel variable indicates which level of bitmap we should use.
-    // Level 0 means the original full-sized bitmap, and a larger value means
-    // a smaller scaled bitmap (The width and height of each scaled bitmap is
-    // half size of the previous one). If the value is in [0, mLevelCount), we
-    // use the bitmap in mScaledBitmaps[mLevel] for display, otherwise the value
-    // is mLevelCount
-    @Thunk int mLevel = 0;
-
-    private int mOffsetX;
-    private int mOffsetY;
-
-    private int mUploadQuota;
-    private boolean mRenderComplete;
-
-    private final RectF mSourceRect = new RectF();
-    private final RectF mTargetRect = new RectF();
-
-    private final LongSparseArray<Tile> mActiveTiles = new LongSparseArray<Tile>();
-
-    // The following three queue are guarded by mQueueLock
-    @Thunk final Object mQueueLock = new Object();
-    private final TileQueue mRecycledQueue = new TileQueue();
-    private final TileQueue mUploadQueue = new TileQueue();
-    @Thunk final TileQueue mDecodeQueue = new TileQueue();
-
-    // The width and height of the full-sized bitmap
-    protected int mImageWidth = SIZE_UNKNOWN;
-    protected int mImageHeight = SIZE_UNKNOWN;
-
-    protected int mCenterX;
-    protected int mCenterY;
-    protected float mScale;
-    protected int mRotation;
-
-    private boolean mLayoutTiles;
-
-    // Temp variables to avoid memory allocation
-    private final Rect mTileRange = new Rect();
-    private final Rect mActiveRange[] = {new Rect(), new Rect()};
-
-    private TileDecoder mTileDecoder;
-    private boolean mBackgroundTileUploaded;
-
-    private int mViewWidth, mViewHeight;
-    private View mParent;
-
-    /**
-     * Interface for providing tiles to a {@link TiledImageRenderer}
-     */
-    public static interface TileSource {
-
-        /**
-         * If the source does not care about the tile size, it should use
-         * {@link TiledImageRenderer#suggestedTileSize(Context)}
-         */
-        public int getTileSize();
-        public int getImageWidth();
-        public int getImageHeight();
-        public int getRotation();
-
-        /**
-         * Return a Preview image if available. This will be used as the base layer
-         * if higher res tiles are not yet available
-         */
-        public BasicTexture getPreview();
-
-        /**
-         * The tile returned by this method can be specified this way: Assuming
-         * the image size is (width, height), first take the intersection of (0,
-         * 0) - (width, height) and (x, y) - (x + tileSize, y + tileSize). If
-         * in extending the region, we found some part of the region is outside
-         * the image, those pixels are filled with black.
-         *
-         * If level > 0, it does the same operation on a down-scaled version of
-         * the original image (down-scaled by a factor of 2^level), but (x, y)
-         * still refers to the coordinate on the original image.
-         *
-         * The method would be called by the decoder thread.
-         */
-        public Bitmap getTile(int level, int x, int y, Bitmap reuse);
-    }
-
-    public static int suggestedTileSize(Context context) {
-        return isHighResolution(context) ? 512 : 256;
-    }
-
-    private static boolean isHighResolution(Context context) {
-        DisplayMetrics metrics = new DisplayMetrics();
-        WindowManager wm = (WindowManager)
-                context.getSystemService(Context.WINDOW_SERVICE);
-        wm.getDefaultDisplay().getMetrics(metrics);
-        return metrics.heightPixels > 2048 ||  metrics.widthPixels > 2048;
-    }
-
-    public TiledImageRenderer(View parent) {
-        mParent = parent;
-        mTileDecoder = new TileDecoder();
-        mTileDecoder.start();
-    }
-
-    public int getViewWidth() {
-        return mViewWidth;
-    }
-
-    public int getViewHeight() {
-        return mViewHeight;
-    }
-
-    private void invalidate() {
-        mParent.postInvalidate();
-    }
-
-    public void setModel(TileSource model, int rotation) {
-        if (mModel != model) {
-            mModel = model;
-            notifyModelInvalidated();
-        }
-        if (mRotation != rotation) {
-            mRotation = rotation;
-            mLayoutTiles = true;
-        }
-    }
-
-    private void calculateLevelCount() {
-        if (mPreview != null) {
-            mLevelCount = Math.max(0, Utils.ceilLog2(
-                mImageWidth / (float) mPreview.getWidth()));
-        } else {
-            int levels = 1;
-            int maxDim = Math.max(mImageWidth, mImageHeight);
-            int t = mTileSize;
-            while (t < maxDim) {
-                t <<= 1;
-                levels++;
-            }
-            mLevelCount = levels;
-        }
-    }
-
-    public void notifyModelInvalidated() {
-        invalidateTiles();
-        if (mModel == null) {
-            mImageWidth = 0;
-            mImageHeight = 0;
-            mLevelCount = 0;
-            mPreview = null;
-        } else {
-            mImageWidth = mModel.getImageWidth();
-            mImageHeight = mModel.getImageHeight();
-            mPreview = mModel.getPreview();
-            mTileSize = mModel.getTileSize();
-            calculateLevelCount();
-        }
-        mLayoutTiles = true;
-    }
-
-    public void setViewSize(int width, int height) {
-        mViewWidth = width;
-        mViewHeight = height;
-    }
-
-    public void setPosition(int centerX, int centerY, float scale) {
-        if (mCenterX == centerX && mCenterY == centerY
-                && mScale == scale) {
-            return;
-        }
-        mCenterX = centerX;
-        mCenterY = centerY;
-        mScale = scale;
-        mLayoutTiles = true;
-    }
-
-    // Prepare the tiles we want to use for display.
-    //
-    // 1. Decide the tile level we want to use for display.
-    // 2. Decide the tile levels we want to keep as texture (in addition to
-    //    the one we use for display).
-    // 3. Recycle unused tiles.
-    // 4. Activate the tiles we want.
-    private void layoutTiles() {
-        if (mViewWidth == 0 || mViewHeight == 0 || !mLayoutTiles) {
-            return;
-        }
-        mLayoutTiles = false;
-
-        // The tile levels we want to keep as texture is in the range
-        // [fromLevel, endLevel).
-        int fromLevel;
-        int endLevel;
-
-        // We want to use a texture larger than or equal to the display size.
-        mLevel = Utils.clamp(Utils.floorLog2(1f / mScale), 0, mLevelCount);
-
-        // We want to keep one more tile level as texture in addition to what
-        // we use for display. So it can be faster when the scale moves to the
-        // next level. We choose the level closest to the current scale.
-        if (mLevel != mLevelCount) {
-            Rect range = mTileRange;
-            getRange(range, mCenterX, mCenterY, mLevel, mScale, mRotation);
-            mOffsetX = Math.round(mViewWidth / 2f + (range.left - mCenterX) * mScale);
-            mOffsetY = Math.round(mViewHeight / 2f + (range.top - mCenterY) * mScale);
-            fromLevel = mScale * (1 << mLevel) > 0.75f ? mLevel - 1 : mLevel;
-        } else {
-            // Activate the tiles of the smallest two levels.
-            fromLevel = mLevel - 2;
-            mOffsetX = Math.round(mViewWidth / 2f - mCenterX * mScale);
-            mOffsetY = Math.round(mViewHeight / 2f - mCenterY * mScale);
-        }
-
-        fromLevel = Math.max(0, Math.min(fromLevel, mLevelCount - 2));
-        endLevel = Math.min(fromLevel + 2, mLevelCount);
-
-        Rect range[] = mActiveRange;
-        for (int i = fromLevel; i < endLevel; ++i) {
-            getRange(range[i - fromLevel], mCenterX, mCenterY, i, mRotation);
-        }
-
-        // If rotation is transient, don't update the tile.
-        if (mRotation % 90 != 0) {
-            return;
-        }
-
-        synchronized (mQueueLock) {
-            mDecodeQueue.clean();
-            mUploadQueue.clean();
-            mBackgroundTileUploaded = false;
-
-            // Recycle unused tiles: if the level of the active tile is outside the
-            // range [fromLevel, endLevel) or not in the visible range.
-            int n = mActiveTiles.size();
-            for (int i = 0; i < n; i++) {
-                Tile tile = mActiveTiles.valueAt(i);
-                int level = tile.mTileLevel;
-                if (level < fromLevel || level >= endLevel
-                        || !range[level - fromLevel].contains(tile.mX, tile.mY)) {
-                    mActiveTiles.removeAt(i);
-                    i--;
-                    n--;
-                    recycleTile(tile);
-                }
-            }
-        }
-
-        for (int i = fromLevel; i < endLevel; ++i) {
-            int size = mTileSize << i;
-            Rect r = range[i - fromLevel];
-            for (int y = r.top, bottom = r.bottom; y < bottom; y += size) {
-                for (int x = r.left, right = r.right; x < right; x += size) {
-                    activateTile(x, y, i);
-                }
-            }
-        }
-        invalidate();
-    }
-
-    private void invalidateTiles() {
-        synchronized (mQueueLock) {
-            mDecodeQueue.clean();
-            mUploadQueue.clean();
-
-            // TODO(xx): disable decoder
-            int n = mActiveTiles.size();
-            for (int i = 0; i < n; i++) {
-                Tile tile = mActiveTiles.valueAt(i);
-                recycleTile(tile);
-            }
-            mActiveTiles.clear();
-        }
-    }
-
-    private void getRange(Rect out, int cX, int cY, int level, int rotation) {
-        getRange(out, cX, cY, level, 1f / (1 << (level + 1)), rotation);
-    }
-
-    // If the bitmap is scaled by the given factor "scale", return the
-    // rectangle containing visible range. The left-top coordinate returned is
-    // aligned to the tile boundary.
-    //
-    // (cX, cY) is the point on the original bitmap which will be put in the
-    // center of the ImageViewer.
-    private void getRange(Rect out,
-            int cX, int cY, int level, float scale, int rotation) {
-
-        double radians = Math.toRadians(-rotation);
-        double w = mViewWidth;
-        double h = mViewHeight;
-
-        double cos = Math.cos(radians);
-        double sin = Math.sin(radians);
-        int width = (int) Math.ceil(Math.max(
-                Math.abs(cos * w - sin * h), Math.abs(cos * w + sin * h)));
-        int height = (int) Math.ceil(Math.max(
-                Math.abs(sin * w + cos * h), Math.abs(sin * w - cos * h)));
-
-        int left = (int) Math.floor(cX - width / (2f * scale));
-        int top = (int) Math.floor(cY - height / (2f * scale));
-        int right = (int) Math.ceil(left + width / scale);
-        int bottom = (int) Math.ceil(top + height / scale);
-
-        // align the rectangle to tile boundary
-        int size = mTileSize << level;
-        left = Math.max(0, size * (left / size));
-        top = Math.max(0, size * (top / size));
-        right = Math.min(mImageWidth, right);
-        bottom = Math.min(mImageHeight, bottom);
-
-        out.set(left, top, right, bottom);
-    }
-
-    public void freeTextures() {
-        mLayoutTiles = true;
-
-        mTileDecoder.finishAndWait();
-        synchronized (mQueueLock) {
-            mUploadQueue.clean();
-            mDecodeQueue.clean();
-            Tile tile = mRecycledQueue.pop();
-            while (tile != null) {
-                tile.recycle();
-                tile = mRecycledQueue.pop();
-            }
-        }
-
-        int n = mActiveTiles.size();
-        for (int i = 0; i < n; i++) {
-            Tile texture = mActiveTiles.valueAt(i);
-            texture.recycle();
-        }
-        mActiveTiles.clear();
-        mTileRange.set(0, 0, 0, 0);
-
-        while (sTilePool.acquire() != null) {}
-    }
-
-    public boolean draw(GLCanvas canvas) {
-        layoutTiles();
-        uploadTiles(canvas);
-
-        mUploadQuota = UPLOAD_LIMIT;
-        mRenderComplete = true;
-
-        int level = mLevel;
-        int rotation = mRotation;
-        int flags = 0;
-        if (rotation != 0) {
-            flags |= GLCanvas.SAVE_FLAG_MATRIX;
-        }
-
-        if (flags != 0) {
-            canvas.save(flags);
-            if (rotation != 0) {
-                int centerX = mViewWidth / 2, centerY = mViewHeight / 2;
-                canvas.translate(centerX, centerY);
-                canvas.rotate(rotation, 0, 0, 1);
-                canvas.translate(-centerX, -centerY);
-            }
-        }
-        try {
-            if (level != mLevelCount) {
-                int size = (mTileSize << level);
-                float length = size * mScale;
-                Rect r = mTileRange;
-
-                for (int ty = r.top, i = 0; ty < r.bottom; ty += size, i++) {
-                    float y = mOffsetY + i * length;
-                    for (int tx = r.left, j = 0; tx < r.right; tx += size, j++) {
-                        float x = mOffsetX + j * length;
-                        drawTile(canvas, tx, ty, level, x, y, length);
-                    }
-                }
-            } else if (mPreview != null) {
-                mPreview.draw(canvas, mOffsetX, mOffsetY,
-                        Math.round(mImageWidth * mScale),
-                        Math.round(mImageHeight * mScale));
-            }
-        } finally {
-            if (flags != 0) {
-                canvas.restore();
-            }
-        }
-
-        if (mRenderComplete) {
-            if (!mBackgroundTileUploaded) {
-                uploadBackgroundTiles(canvas);
-            }
-        } else {
-            invalidate();
-        }
-        return mRenderComplete || mPreview != null;
-    }
-
-    private void uploadBackgroundTiles(GLCanvas canvas) {
-        mBackgroundTileUploaded = true;
-        int n = mActiveTiles.size();
-        for (int i = 0; i < n; i++) {
-            Tile tile = mActiveTiles.valueAt(i);
-            if (!tile.isContentValid()) {
-                queueForDecode(tile);
-            }
-        }
-    }
-
-   private void queueForDecode(Tile tile) {
-       synchronized (mQueueLock) {
-           if (tile.mTileState == STATE_ACTIVATED) {
-               tile.mTileState = STATE_IN_QUEUE;
-               if (mDecodeQueue.push(tile)) {
-                   mQueueLock.notifyAll();
-               }
-           }
-       }
-    }
-
-    @Thunk void decodeTile(Tile tile) {
-        synchronized (mQueueLock) {
-            if (tile.mTileState != STATE_IN_QUEUE) {
-                return;
-            }
-            tile.mTileState = STATE_DECODING;
-        }
-        boolean decodeComplete = tile.decode();
-        synchronized (mQueueLock) {
-            if (tile.mTileState == STATE_RECYCLING) {
-                tile.mTileState = STATE_RECYCLED;
-                if (tile.mDecodedTile != null) {
-                    sTilePool.release(tile.mDecodedTile);
-                    tile.mDecodedTile = null;
-                }
-                mRecycledQueue.push(tile);
-                return;
-            }
-            tile.mTileState = decodeComplete ? STATE_DECODED : STATE_DECODE_FAIL;
-            if (!decodeComplete) {
-                return;
-            }
-            mUploadQueue.push(tile);
-        }
-        invalidate();
-    }
-
-    private Tile obtainTile(int x, int y, int level) {
-        synchronized (mQueueLock) {
-            Tile tile = mRecycledQueue.pop();
-            if (tile != null) {
-                tile.mTileState = STATE_ACTIVATED;
-                tile.update(x, y, level);
-                return tile;
-            }
-            return new Tile(x, y, level);
-        }
-    }
-
-    private void recycleTile(Tile tile) {
-        synchronized (mQueueLock) {
-            if (tile.mTileState == STATE_DECODING) {
-                tile.mTileState = STATE_RECYCLING;
-                return;
-            }
-            tile.mTileState = STATE_RECYCLED;
-            if (tile.mDecodedTile != null) {
-                sTilePool.release(tile.mDecodedTile);
-                tile.mDecodedTile = null;
-            }
-            mRecycledQueue.push(tile);
-        }
-    }
-
-    private void activateTile(int x, int y, int level) {
-        long key = makeTileKey(x, y, level);
-        Tile tile = mActiveTiles.get(key);
-        if (tile != null) {
-            if (tile.mTileState == STATE_IN_QUEUE) {
-                tile.mTileState = STATE_ACTIVATED;
-            }
-            return;
-        }
-        tile = obtainTile(x, y, level);
-        mActiveTiles.put(key, tile);
-    }
-
-    @Thunk Tile getTile(int x, int y, int level) {
-        return mActiveTiles.get(makeTileKey(x, y, level));
-    }
-
-    private static long makeTileKey(int x, int y, int level) {
-        long result = x;
-        result = (result << 16) | y;
-        result = (result << 16) | level;
-        return result;
-    }
-
-    private void uploadTiles(GLCanvas canvas) {
-        int quota = UPLOAD_LIMIT;
-        Tile tile = null;
-        while (quota > 0) {
-            synchronized (mQueueLock) {
-                tile = mUploadQueue.pop();
-            }
-            if (tile == null) {
-                break;
-            }
-            if (!tile.isContentValid()) {
-                if (tile.mTileState == STATE_DECODED) {
-                    tile.updateContent(canvas);
-                    --quota;
-                } else {
-                    Log.w(TAG, "Tile in upload queue has invalid state: " + tile.mTileState);
-                }
-            }
-        }
-        if (tile != null) {
-            invalidate();
-        }
-    }
-
-    // Draw the tile to a square at canvas that locates at (x, y) and
-    // has a side length of length.
-    private void drawTile(GLCanvas canvas,
-            int tx, int ty, int level, float x, float y, float length) {
-        RectF source = mSourceRect;
-        RectF target = mTargetRect;
-        target.set(x, y, x + length, y + length);
-        source.set(0, 0, mTileSize, mTileSize);
-
-        Tile tile = getTile(tx, ty, level);
-        if (tile != null) {
-            if (!tile.isContentValid()) {
-                if (tile.mTileState == STATE_DECODED) {
-                    if (mUploadQuota > 0) {
-                        --mUploadQuota;
-                        tile.updateContent(canvas);
-                    } else {
-                        mRenderComplete = false;
-                    }
-                } else if (tile.mTileState != STATE_DECODE_FAIL){
-                    mRenderComplete = false;
-                    queueForDecode(tile);
-                }
-            }
-            if (drawTile(tile, canvas, source, target)) {
-                return;
-            }
-        }
-        if (mPreview != null) {
-            int size = mTileSize << level;
-            float scaleX = (float) mPreview.getWidth() / mImageWidth;
-            float scaleY = (float) mPreview.getHeight() / mImageHeight;
-            source.set(tx * scaleX, ty * scaleY, (tx + size) * scaleX,
-                    (ty + size) * scaleY);
-            canvas.drawTexture(mPreview, source, target);
-        }
-    }
-
-    private boolean drawTile(
-            Tile tile, GLCanvas canvas, RectF source, RectF target) {
-        while (true) {
-            if (tile.isContentValid()) {
-                canvas.drawTexture(tile, source, target);
-                return true;
-            }
-
-            // Parent can be divided to four quads and tile is one of the four.
-            Tile parent = tile.getParentTile();
-            if (parent == null) {
-                return false;
-            }
-            if (tile.mX == parent.mX) {
-                source.left /= 2f;
-                source.right /= 2f;
-            } else {
-                source.left = (mTileSize + source.left) / 2f;
-                source.right = (mTileSize + source.right) / 2f;
-            }
-            if (tile.mY == parent.mY) {
-                source.top /= 2f;
-                source.bottom /= 2f;
-            } else {
-                source.top = (mTileSize + source.top) / 2f;
-                source.bottom = (mTileSize + source.bottom) / 2f;
-            }
-            tile = parent;
-        }
-    }
-
-    private class Tile extends UploadedTexture {
-        public int mX;
-        public int mY;
-        public int mTileLevel;
-        public Tile mNext;
-        public Bitmap mDecodedTile;
-        public volatile int mTileState = STATE_ACTIVATED;
-
-        public Tile(int x, int y, int level) {
-            mX = x;
-            mY = y;
-            mTileLevel = level;
-        }
-
-        @Override
-        protected void onFreeBitmap(Bitmap bitmap) {
-            sTilePool.release(bitmap);
-        }
-
-        boolean decode() {
-            // Get a tile from the original image. The tile is down-scaled
-            // by (1 << mTilelevel) from a region in the original image.
-            try {
-                Bitmap reuse = sTilePool.acquire();
-                if (reuse != null && reuse.getWidth() != mTileSize) {
-                    reuse = null;
-                }
-                mDecodedTile = mModel.getTile(mTileLevel, mX, mY, reuse);
-            } catch (Throwable t) {
-                Log.w(TAG, "fail to decode tile", t);
-            }
-            return mDecodedTile != null;
-        }
-
-        @Override
-        protected Bitmap onGetBitmap() {
-            Utils.assertTrue(mTileState == STATE_DECODED);
-
-            // We need to override the width and height, so that we won't
-            // draw beyond the boundaries.
-            int rightEdge = ((mImageWidth - mX) >> mTileLevel);
-            int bottomEdge = ((mImageHeight - mY) >> mTileLevel);
-            setSize(Math.min(mTileSize, rightEdge), Math.min(mTileSize, bottomEdge));
-
-            Bitmap bitmap = mDecodedTile;
-            mDecodedTile = null;
-            mTileState = STATE_ACTIVATED;
-            return bitmap;
-        }
-
-        // We override getTextureWidth() and getTextureHeight() here, so the
-        // texture can be re-used for different tiles regardless of the actual
-        // size of the tile (which may be small because it is a tile at the
-        // boundary).
-        @Override
-        public int getTextureWidth() {
-            return mTileSize;
-        }
-
-        @Override
-        public int getTextureHeight() {
-            return mTileSize;
-        }
-
-        public void update(int x, int y, int level) {
-            mX = x;
-            mY = y;
-            mTileLevel = level;
-            invalidateContent();
-        }
-
-        public Tile getParentTile() {
-            if (mTileLevel + 1 == mLevelCount) {
-                return null;
-            }
-            int size = mTileSize << (mTileLevel + 1);
-            int x = size * (mX / size);
-            int y = size * (mY / size);
-            return getTile(x, y, mTileLevel + 1);
-        }
-
-        @Override
-        public String toString() {
-            return String.format("tile(%s, %s, %s / %s)",
-                    mX / mTileSize, mY / mTileSize, mLevel, mLevelCount);
-        }
-    }
-
-    @Thunk static class TileQueue {
-        private Tile mHead;
-
-        public Tile pop() {
-            Tile tile = mHead;
-            if (tile != null) {
-                mHead = tile.mNext;
-            }
-            return tile;
-        }
-
-        public boolean push(Tile tile) {
-            if (contains(tile)) {
-                Log.w(TAG, "Attempting to add a tile already in the queue!");
-                return false;
-            }
-            boolean wasEmpty = mHead == null;
-            tile.mNext = mHead;
-            mHead = tile;
-            return wasEmpty;
-        }
-
-        private boolean contains(Tile tile) {
-            Tile other = mHead;
-            while (other != null) {
-                if (other == tile) {
-                    return true;
-                }
-                other = other.mNext;
-            }
-            return false;
-        }
-
-        public void clean() {
-            mHead = null;
-        }
-    }
-
-    @Thunk class TileDecoder extends Thread {
-
-        public void finishAndWait() {
-            interrupt();
-            try {
-                join();
-            } catch (InterruptedException e) {
-                Log.w(TAG, "Interrupted while waiting for TileDecoder thread to finish!");
-            }
-        }
-
-        private Tile waitForTile() throws InterruptedException {
-            synchronized (mQueueLock) {
-                while (true) {
-                    Tile tile = mDecodeQueue.pop();
-                    if (tile != null) {
-                        return tile;
-                    }
-                    mQueueLock.wait();
-                }
-            }
-        }
-
-        @Override
-        public void run() {
-            try {
-                while (!isInterrupted()) {
-                    Tile tile = waitForTile();
-                    decodeTile(tile);
-                }
-            } catch (InterruptedException ex) {
-                // We were finished
-            }
-        }
-
-    }
-}
diff --git a/WallpaperPicker/src/com/android/photos/views/TiledImageView.java b/WallpaperPicker/src/com/android/photos/views/TiledImageView.java
deleted file mode 100644
index 7e3e1a9..0000000
--- a/WallpaperPicker/src/com/android/photos/views/TiledImageView.java
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.photos.views;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
-import android.graphics.RectF;
-import android.opengl.GLSurfaceView;
-import android.opengl.GLSurfaceView.Renderer;
-import android.util.AttributeSet;
-import android.view.Choreographer;
-import android.view.Choreographer.FrameCallback;
-import android.widget.FrameLayout;
-
-import com.android.gallery3d.glrenderer.BasicTexture;
-import com.android.gallery3d.glrenderer.GLES20Canvas;
-import com.android.launcher3.util.Thunk;
-import com.android.photos.views.TiledImageRenderer.TileSource;
-
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.opengles.GL10;
-
-/**
- * Shows an image using {@link TiledImageRenderer} using either {@link GLSurfaceView}.
- */
-public class TiledImageView extends FrameLayout {
-
-    @Thunk GLSurfaceView mGLSurfaceView;
-    @Thunk boolean mInvalPending = false;
-    private FrameCallback mFrameCallback;
-
-    protected static class ImageRendererWrapper {
-        // Guarded by locks
-        public float scale;
-        public int centerX, centerY;
-        public int rotation;
-        public TileSource source;
-        Runnable isReadyCallback;
-
-        // GL thread only
-        TiledImageRenderer image;
-    }
-
-    private float[] mValues = new float[9];
-
-    // -------------------------
-    // Guarded by mLock
-    // -------------------------
-    protected Object mLock = new Object();
-    protected ImageRendererWrapper mRenderer;
-
-    public TiledImageView(Context context) {
-        this(context, null);
-    }
-
-    public TiledImageView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        mRenderer = new ImageRendererWrapper();
-        mRenderer.image = new TiledImageRenderer(this);
-        mGLSurfaceView = new GLSurfaceView(context);
-        mGLSurfaceView.setEGLContextClientVersion(2);
-        mGLSurfaceView.setRenderer(new TileRenderer());
-        mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
-        addView(mGLSurfaceView, new LayoutParams(
-                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
-        //setTileSource(new ColoredTiles());
-    }
-
-    @Override
-    public void setVisibility(int visibility) {
-        super.setVisibility(visibility);
-        // need to update inner view's visibility because it seems like we're causing it to draw
-        // from {@link #dispatchDraw} or {@link #invalidate} even if we are invisible.
-        mGLSurfaceView.setVisibility(visibility);
-    }
-
-    public void destroy() {
-        mGLSurfaceView.queueEvent(mFreeTextures);
-    }
-
-    private Runnable mFreeTextures = new Runnable() {
-
-        @Override
-        public void run() {
-            mRenderer.image.freeTextures();
-        }
-    };
-
-    public void onPause() {
-        mGLSurfaceView.onPause();
-    }
-
-    public void onResume() {
-        mGLSurfaceView.onResume();
-    }
-
-    public void setTileSource(TileSource source, Runnable isReadyCallback) {
-        synchronized (mLock) {
-            mRenderer.source = source;
-            mRenderer.isReadyCallback = isReadyCallback;
-            mRenderer.centerX = source != null ? source.getImageWidth() / 2 : 0;
-            mRenderer.centerY = source != null ? source.getImageHeight() / 2 : 0;
-            mRenderer.rotation = source != null ? source.getRotation() : 0;
-            mRenderer.scale = 0;
-            updateScaleIfNecessaryLocked(mRenderer);
-        }
-        invalidate();
-    }
-
-    public TileSource getTileSource() {
-        return mRenderer.source;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right,
-            int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        synchronized (mLock) {
-            updateScaleIfNecessaryLocked(mRenderer);
-        }
-    }
-
-    private void updateScaleIfNecessaryLocked(ImageRendererWrapper renderer) {
-        if (renderer == null || renderer.source == null
-                || renderer.scale > 0 || getWidth() == 0) {
-            return;
-        }
-        renderer.scale = Math.min(
-                (float) getWidth() / (float) renderer.source.getImageWidth(),
-                (float) getHeight() / (float) renderer.source.getImageHeight());
-    }
-
-    @Override
-    public void invalidate() {
-        invalOnVsync();
-    }
-
-    private void invalOnVsync() {
-        if (!mInvalPending) {
-            mInvalPending = true;
-            if (mFrameCallback == null) {
-                mFrameCallback = new FrameCallback() {
-                    @Override
-                    public void doFrame(long frameTimeNanos) {
-                        mInvalPending = false;
-                        mGLSurfaceView.requestRender();
-                    }
-                };
-            }
-            Choreographer.getInstance().postFrameCallback(mFrameCallback);
-        }
-    }
-
-    private RectF mTempRectF = new RectF();
-    public void positionFromMatrix(Matrix matrix) {
-        if (mRenderer.source != null) {
-            final int rotation = mRenderer.source.getRotation();
-            final boolean swap = !(rotation % 180 == 0);
-            final int width = swap ? mRenderer.source.getImageHeight()
-                    : mRenderer.source.getImageWidth();
-            final int height = swap ? mRenderer.source.getImageWidth()
-                    : mRenderer.source.getImageHeight();
-            mTempRectF.set(0, 0, width, height);
-            matrix.mapRect(mTempRectF);
-            matrix.getValues(mValues);
-            int cx = width / 2;
-            int cy = height / 2;
-            float scale = mValues[Matrix.MSCALE_X];
-            int xoffset = Math.round((getWidth() - mTempRectF.width()) / 2 / scale);
-            int yoffset = Math.round((getHeight() - mTempRectF.height()) / 2 / scale);
-            if (rotation == 90 || rotation == 180) {
-                cx += (mTempRectF.left / scale) - xoffset;
-            } else {
-                cx -= (mTempRectF.left / scale) - xoffset;
-            }
-            if (rotation == 180 || rotation == 270) {
-                cy += (mTempRectF.top / scale) - yoffset;
-            } else {
-                cy -= (mTempRectF.top / scale) - yoffset;
-            }
-            mRenderer.scale = scale;
-            mRenderer.centerX = swap ? cy : cx;
-            mRenderer.centerY = swap ? cx : cy;
-            invalidate();
-        }
-    }
-
-    @Thunk class TileRenderer implements Renderer {
-
-        private GLES20Canvas mCanvas;
-
-        @Override
-        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
-            mCanvas = new GLES20Canvas();
-            BasicTexture.invalidateAllTextures();
-            mRenderer.image.setModel(mRenderer.source, mRenderer.rotation);
-        }
-
-        @Override
-        public void onSurfaceChanged(GL10 gl, int width, int height) {
-            mCanvas.setSize(width, height);
-            mRenderer.image.setViewSize(width, height);
-        }
-
-        @Override
-        public void onDrawFrame(GL10 gl) {
-            mCanvas.clearBuffer();
-            Runnable readyCallback;
-            synchronized (mLock) {
-                readyCallback = mRenderer.isReadyCallback;
-                mRenderer.image.setModel(mRenderer.source, mRenderer.rotation);
-                mRenderer.image.setPosition(mRenderer.centerX, mRenderer.centerY,
-                        mRenderer.scale);
-            }
-            boolean complete = mRenderer.image.draw(mCanvas);
-            if (complete && readyCallback != null) {
-                synchronized (mLock) {
-                    // Make sure we don't trample on a newly set callback/source
-                    // if it changed while we were rendering
-                    if (mRenderer.isReadyCallback == readyCallback) {
-                        mRenderer.isReadyCallback = null;
-                    }
-                }
-                if (readyCallback != null) {
-                    post(readyCallback);
-                }
-            }
-        }
-
-    }
-
-    @SuppressWarnings("unused")
-    private static class ColoredTiles implements TileSource {
-        private static final int[] COLORS = new int[] {
-            Color.RED,
-            Color.BLUE,
-            Color.YELLOW,
-            Color.GREEN,
-            Color.CYAN,
-            Color.MAGENTA,
-            Color.WHITE,
-        };
-
-        private Paint mPaint = new Paint();
-        private Canvas mCanvas = new Canvas();
-
-        @Override
-        public int getTileSize() {
-            return 256;
-        }
-
-        @Override
-        public int getImageWidth() {
-            return 16384;
-        }
-
-        @Override
-        public int getImageHeight() {
-            return 8192;
-        }
-
-        @Override
-        public int getRotation() {
-            return 0;
-        }
-
-        @Override
-        public Bitmap getTile(int level, int x, int y, Bitmap bitmap) {
-            int tileSize = getTileSize();
-            if (bitmap == null) {
-                bitmap = Bitmap.createBitmap(tileSize, tileSize,
-                        Bitmap.Config.ARGB_8888);
-            }
-            mCanvas.setBitmap(bitmap);
-            mCanvas.drawColor(COLORS[level]);
-            mPaint.setColor(Color.BLACK);
-            mPaint.setTextSize(20);
-            mPaint.setTextAlign(Align.CENTER);
-            mCanvas.drawText(x + "x" + y, 128, 128, mPaint);
-            tileSize <<= level;
-            x /= tileSize;
-            y /= tileSize;
-            mCanvas.drawText(x + "x" + y + " @ " + level, 128, 30, mPaint);
-            mCanvas.setBitmap(null);
-            return bitmap;
-        }
-
-        @Override
-        public BasicTexture getPreview() {
-            return null;
-        }
-    }
-}
diff --git a/build.gradle b/build.gradle
index b9a990f..e103d79 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,8 +3,8 @@
         mavenCentral()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:1.5.0'
-        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.7.0'
+        classpath 'com.android.tools.build:gradle:2.1.3'
+        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
     }
 }
 
@@ -12,17 +12,15 @@
 apply plugin: 'com.google.protobuf'
 
 android {
-    compileSdkVersion 23
-    buildToolsVersion "22.0.1"
+    compileSdkVersion 25
+    buildToolsVersion '24.0.0'
 
     defaultConfig {
-        applicationId "com.android.launcher3"
-        minSdkVersion 16
-        targetSdkVersion 23
+        minSdkVersion 21
+        targetSdkVersion 25
         versionCode 1
         versionName "1.0"
 
-        testApplicationId "com.android.launcher3.tests"
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     }
     buildTypes {
@@ -30,19 +28,29 @@
             minifyEnabled false
         }
     }
+
+    productFlavors {
+        aosp {
+            applicationId 'com.android.launcher3'
+            testApplicationId 'com.android.launcher3.tests'
+        }
+    }
     sourceSets {
         main {
-            res.srcDirs = ['res', 'WallpaperPicker/res']
-            java.srcDirs = ['src', 'WallpaperPicker/src']
-            manifest.srcFile 'AndroidManifest.xml'
+            res.srcDirs = ['res']
+            java.srcDirs = ['src', 'src_config']
+            manifest.srcFile 'AndroidManifest-common.xml'
             proto.srcDirs 'protos/'
         }
 
         androidTest {
             java.srcDirs = ['tests/src']
-            res.srcDirs = ['tests/res']
             manifest.srcFile "tests/AndroidManifest.xml"
         }
+
+        aosp {
+            manifest.srcFile "AndroidManifest.xml"
+        }
     }
 }
 
@@ -53,16 +61,29 @@
 dependencies {
     compile 'com.android.support:support-v4:23.1.1'
     compile 'com.android.support:recyclerview-v7:23.1.1'
+    compile 'com.android.support:palette-v7:23.2.0'
     compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-2'
 
     testCompile 'junit:junit:4.12'
     androidTestCompile 'com.android.support.test:runner:0.5'
     androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
+    androidTestCompile 'com.android.support:support-annotations:23.2.0'
 }
 
 protobuf {
     // Configure the protoc executable
     protoc {
         artifact = 'com.google.protobuf:protoc:3.0.0-alpha-3'
+
+        generateProtoTasks {
+            all().each { task ->
+                task.builtins {
+                    remove java
+                    javanano {
+                        option 'ignore_services=false'
+                    }
+                }
+            }
+        }
     }
 }
diff --git a/proguard.flags b/proguard.flags
index 15faed9..c5e9db1 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -1,3 +1,7 @@
+-keep,allowshrinking,allowoptimization class com.android.launcher3.** {
+  *;
+}
+
 -keep class com.android.launcher3.allapps.AllAppsBackgroundDrawable {
   public void setAlpha(int);
   public int getAlpha();
@@ -35,7 +39,7 @@
   public int getY();
 }
 
--keep class com.android.launcher3.DragLayer$LayoutParams {
+-keep class com.android.launcher3.dragndrop.DragLayer$LayoutParams {
   public void setWidth(int);
   public int getWidth();
   public void setHeight(int);
@@ -62,6 +66,11 @@
   public void setAnimationProgress(float);
 }
 
+-keep class com.android.launcher3.pageindicators.CaretDrawable {
+  public float getCaretProgress();
+  public void setCaretProgress(float);
+}
+
 -keep class com.android.launcher3.Workspace {
   public float getBackgroundAlpha();
   public void setBackgroundAlpha(float);
diff --git a/protos/backup.proto b/protos/backup.proto
deleted file mode 100644
index c3f27e1..0000000
--- a/protos/backup.proto
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-
-package launcher_backup;
-
-option java_package = "com.android.launcher3.backup.nano";
-option java_outer_classname = "BackupProtos";
-
-message Key {
-  enum Type {
-    FAVORITE = 1;
-    SCREEN = 2;
-    ICON = 3;
-    WIDGET = 4;
-  }
-  required Type type = 1;
-  optional string name = 2;  // keep this short
-  optional int64 id = 3;
-  optional int64 checksum = 4;
-}
-
-message CheckedMessage {
-  required bytes payload = 1;
-  required int64 checksum = 2;
-}
-
-message DeviceProfieData {
-  required float desktop_rows = 1;
-  required float desktop_cols = 2;
-  required float hotseat_count = 3;
-  required int32 allapps_rank = 4;
-}
-
-message Journal {
-  required int32 app_version = 1;
-
-  // Time when the backup was created
-  required int64 t = 2;
-
-  // Total bytes written during the last backup
-  // OBSOLETE: A state may contain entries which are already present in the backup
-  // and were not written in the last backup
-  optional int64 bytes = 3;
-
-  // Total entries written during the last backup
-  // OBSOLETE: A state may contain entries which are already present in the backup
-  // and were not written in the last backup
-  optional int32 rows = 4;
-
-  // Valid keys for this state
-  repeated Key key = 5;
-
-  // Backup format version.
-  optional int32 backup_version = 6 [default = 1];
-
-  optional DeviceProfieData profile = 7;
-}
-
-message Favorite {
-  // Type of the app, this target represents
-  enum TargetType {
-    TARGET_NONE = 0;
-    TARGET_PHONE = 1;
-    TARGET_MESSENGER = 2;
-    TARGET_EMAIL = 3;
-    TARGET_BROWSER = 4;
-    TARGET_GALLERY = 5;
-    TARGET_CAMERA = 6;
-  }
-
-  required int64 id = 1;
-  required int32 itemType = 2;
-  optional string title = 3;
-  optional int32 container = 4;
-  optional int32 screen = 5;
-  optional int32 cellX = 6;
-  optional int32 cellY = 7;
-  optional int32 spanX = 8;
-  optional int32 spanY = 9;
-  optional int32 displayMode = 10;
-  optional int32 appWidgetId = 11;
-  optional string appWidgetProvider = 12;
-  optional string intent = 13;
-  optional string uri = 14;
-  optional int32 iconType = 15;
-  optional string iconPackage = 16;
-  optional string iconResource = 17;
-  optional bytes icon = 18;
-
-  // Added in backup version 4
-  optional TargetType targetType = 19 [default = TARGET_NONE];
-  optional int32 rank = 20;
-}
-
-message Screen {
-  required int64 id = 1;
-  optional int32 rank = 2;
-}
-
-message Resource {
-  required int32 dpi = 1;
-  required bytes data = 2;
-}
-
-message Widget {
-  required string provider = 1;
-  optional string label = 2;
-  optional bool configure = 3;
-  optional Resource icon = 4;
-  optional Resource preview = 5;
-
-  // Added in backup version 3
-  // Assume that a widget is resizable upto 2x2 if no data is available
-  optional int32 minSpanX = 6 [default = 2];
-  optional int32 minSpanY = 7 [default = 2];
-}
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
new file mode 100644
index 0000000..448cf64
--- /dev/null
+++ b/protos/launcher_log.proto
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+syntax = "proto2";
+
+option java_package = "com.android.launcher3.userevent.nano";
+option java_outer_classname = "LauncherLogProto";
+
+package userevent;
+
+message Target {
+  enum Type {
+    NONE = 0;
+    ITEM = 1;
+    CONTROL = 2;
+    CONTAINER = 3;
+  }
+
+  optional Type type = 1;
+
+  // For container type and item type
+  // Used mainly for ContainerType.FOLDER, ItemType.*
+  optional int32 page_index = 2;
+  optional int32 rank = 3;
+  optional int32 grid_x = 4;
+  optional int32 grid_y = 5;
+
+  // For container types only
+  optional ContainerType container_type = 6;
+  optional int32 cardinality = 7;
+
+  // For control types only
+  optional ControlType control_type = 8;
+
+  // For item types only
+  optional ItemType item_type = 9;
+  optional int32 package_name_hash = 10;
+  optional int32 component_hash = 11;      // Used for ItemType.WIDGET
+  optional int32 intent_hash = 12;         // Used for ItemType.SHORTCUT
+  optional int32 span_x = 13 [default = 1];// Used for ItemType.WIDGET
+  optional int32 span_y = 14 [default = 1];// Used for ItemType.WIDGET
+  optional int32 predictedRank = 15;
+}
+
+// Used to define what type of item a Target would represent.
+enum ItemType {
+  DEFAULT_ITEMTYPE = 0;
+  APP_ICON = 1;
+  SHORTCUT = 2;
+  WIDGET = 3;
+  FOLDER_ICON = 4;
+  DEEPSHORTCUT = 5;
+  SEARCHBOX = 6;
+}
+
+// Used to define what type of container a Target would represent.
+enum ContainerType {
+  DEFAULT_CONTAINERTYPE = 0;
+  WORKSPACE = 1;
+  HOTSEAT = 2;
+  FOLDER = 3;
+  ALLAPPS = 4;
+  WIDGETS = 5;
+  OVERVIEW = 6;
+  PREDICTION = 7;
+  SEARCHRESULT = 8;
+  DEEPSHORTCUTS = 9;
+}
+
+// Used to define what type of control a Target would represent.
+enum ControlType {
+  DEFAULT_CONTROLTYPE = 0;
+  ALL_APPS_BUTTON = 1;
+  WIDGETS_BUTTON = 2;
+  WALLPAPER_BUTTON = 3;
+  SETTINGS_BUTTON = 4;
+  REMOVE_TARGET = 5;
+  UNINSTALL_TARGET = 6;
+  APPINFO_TARGET = 7;
+  RESIZE_HANDLE = 8;
+  VERTICAL_SCROLL = 9;
+  // HOME, BACK, GO_TO_PLAYSTORE
+}
+
+// Used to define the action component of the LauncherEvent.
+message Action {
+  enum Type {
+    TOUCH = 0;
+    AUTOMATED = 1;
+    // SOFT_KEYBOARD, HARD_KEYBOARD, ASSIST
+  }
+  enum Touch {
+    TAP = 0;
+    LONGPRESS = 1;
+    DRAGDROP = 2;
+    SWIPE = 3;
+    FLING = 4;
+    PINCH = 5;
+  }
+ enum Direction {
+    NONE = 0;
+    UP = 1;
+    DOWN = 2;
+    LEFT = 3;
+    RIGHT = 4;
+  }
+  optional Type type = 1;
+  optional Touch touch = 2;
+  optional Direction dir = 3;
+}
+
+//
+// Context free grammar of typical user interaction:
+//         Action (Touch) + Target
+//         Action (Touch) + Target + Target
+//
+message LauncherEvent {
+
+  required Action action = 1;
+
+  // List of targets that touch actions can be operated on.
+  repeated Target src_target = 2;
+  repeated Target dest_target = 3;
+
+  optional int64 action_duration_millis = 4;
+  optional int64 elapsed_container_millis = 5;
+  optional int64 elapsed_session_millis = 6;
+}
diff --git a/res/anim/discovery_bounce.xml b/res/anim/discovery_bounce.xml
new file mode 100644
index 0000000..1f7d466
--- /dev/null
+++ b/res/anim/discovery_bounce.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2016, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+        android:ordering="sequentially"
+        android:startOffset="200">
+
+    <objectAnimator
+        android:propertyName="progress"
+        android:duration="250"
+        android:interpolator="@interpolator/disco_bounce_section1"
+        android:valueFrom="1f"
+        android:valueTo=".94f"
+        android:valueType="floatType"/>
+    <objectAnimator
+        android:propertyName="progress"
+        android:duration="216"
+        android:interpolator="@interpolator/disco_bounce_section2"
+        android:valueFrom=".94f"
+        android:valueTo="1.012f"
+        android:valueType="floatType"/>
+    <objectAnimator
+        android:propertyName="progress"
+        android:duration="234"
+        android:interpolator="@interpolator/disco_bounce_section3"
+        android:valueFrom="1.012f"
+        android:valueTo="1f"
+        android:valueType="floatType"/>
+</set>
diff --git a/res/animator-v21/overview_button_anim.xml b/res/animator-v21/overview_button_anim.xml
new file mode 100644
index 0000000..aac3d26
--- /dev/null
+++ b/res/animator-v21/overview_button_anim.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2015, 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true">
+        <objectAnimator
+            android:duration="@android:integer/config_shortAnimTime"
+            android:propertyName="alpha"
+            android:valueTo="0.5"
+            android:valueType="floatType" />
+    </item>
+
+    <item android:state_focused="true">
+        <objectAnimator
+            android:duration="@android:integer/config_shortAnimTime"
+            android:propertyName="alpha"
+            android:valueTo="0.5"
+            android:valueType="floatType" />
+    </item>
+    <item>
+        <objectAnimator
+            android:duration="@android:integer/config_shortAnimTime"
+            android:propertyName="alpha"
+            android:valueTo="1"
+            android:valueType="floatType" />
+    </item>
+
+</selector>
\ No newline at end of file
diff --git a/res/drawable-hdpi/cling_bg.9.png b/res/drawable-hdpi/cling_bg.9.png
deleted file mode 100644
index fb101f4..0000000
--- a/res/drawable-hdpi/cling_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_all_apps_bg_hand.png b/res/drawable-hdpi/ic_all_apps_bg_hand.png
index 43b1bed..dff2f54 100644
--- a/res/drawable-hdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-hdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_all_apps_bg_icon_1.png b/res/drawable-hdpi/ic_all_apps_bg_icon_1.png
deleted file mode 100644
index d2c4cc1..0000000
--- a/res/drawable-hdpi/ic_all_apps_bg_icon_1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_all_apps_bg_icon_2.png b/res/drawable-hdpi/ic_all_apps_bg_icon_2.png
deleted file mode 100644
index 57b7456..0000000
--- a/res/drawable-hdpi/ic_all_apps_bg_icon_2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_all_apps_bg_icon_3.png b/res/drawable-hdpi/ic_all_apps_bg_icon_3.png
deleted file mode 100644
index 54fe70b..0000000
--- a/res/drawable-hdpi/ic_all_apps_bg_icon_3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_all_apps_bg_icon_4.png b/res/drawable-hdpi/ic_all_apps_bg_icon_4.png
deleted file mode 100644
index 9c0f777..0000000
--- a/res/drawable-hdpi/ic_all_apps_bg_icon_4.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_arrow_back_grey.png b/res/drawable-hdpi/ic_arrow_back_grey.png
deleted file mode 100755
index 7d7bfb1..0000000
--- a/res/drawable-hdpi/ic_arrow_back_grey.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_add.png b/res/drawable-hdpi/ic_pageindicator_add.png
deleted file mode 100644
index 6e3f5af..0000000
--- a/res/drawable-hdpi/ic_pageindicator_add.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_current.png b/res/drawable-hdpi/ic_pageindicator_current.png
deleted file mode 100644
index 6dbc4f9..0000000
--- a/res/drawable-hdpi/ic_pageindicator_current.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_current_folder.png b/res/drawable-hdpi/ic_pageindicator_current_folder.png
deleted file mode 100644
index c6c4228..0000000
--- a/res/drawable-hdpi/ic_pageindicator_current_folder.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_default.png b/res/drawable-hdpi/ic_pageindicator_default.png
deleted file mode 100644
index 19945a5..0000000
--- a/res/drawable-hdpi/ic_pageindicator_default.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_default_folder.png b/res/drawable-hdpi/ic_pageindicator_default_folder.png
deleted file mode 100644
index 4710374..0000000
--- a/res/drawable-hdpi/ic_pageindicator_default_folder.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_search_grey.png b/res/drawable-hdpi/ic_search_grey.png
deleted file mode 100755
index bc50a47..0000000
--- a/res/drawable-hdpi/ic_search_grey.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_setting.png b/res/drawable-hdpi/ic_setting.png
deleted file mode 100644
index 72a9745..0000000
--- a/res/drawable-hdpi/ic_setting.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_setting_pressed.png b/res/drawable-hdpi/ic_setting_pressed.png
deleted file mode 100644
index b86fce1..0000000
--- a/res/drawable-hdpi/ic_setting_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_wallpaper.png b/res/drawable-hdpi/ic_wallpaper.png
deleted file mode 100644
index 5936059..0000000
--- a/res/drawable-hdpi/ic_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_wallpaper_pressed.png b/res/drawable-hdpi/ic_wallpaper_pressed.png
deleted file mode 100644
index 4bb1958..0000000
--- a/res/drawable-hdpi/ic_wallpaper_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_widget.png b/res/drawable-hdpi/ic_widget.png
deleted file mode 100644
index 172664b..0000000
--- a/res/drawable-hdpi/ic_widget.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_widget_pressed.png b/res/drawable-hdpi/ic_widget_pressed.png
deleted file mode 100644
index 7f31ab3..0000000
--- a/res/drawable-hdpi/ic_widget_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/portal_ring_inner.png b/res/drawable-hdpi/portal_ring_inner.png
deleted file mode 100644
index 65f5af2..0000000
--- a/res/drawable-hdpi/portal_ring_inner.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/portal_ring_inner_nolip.png b/res/drawable-hdpi/portal_ring_inner_nolip.png
deleted file mode 100644
index 5be25fc..0000000
--- a/res/drawable-hdpi/portal_ring_inner_nolip.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/portal_ring_outer.png b/res/drawable-hdpi/portal_ring_outer.png
deleted file mode 100644
index 712eeb2..0000000
--- a/res/drawable-hdpi/portal_ring_outer.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/portal_ring_rest.png b/res/drawable-hdpi/portal_ring_rest.png
deleted file mode 100644
index 33cec32..0000000
--- a/res/drawable-hdpi/portal_ring_rest.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/workspace_bg.9.png b/res/drawable-hdpi/workspace_bg.9.png
old mode 100644
new mode 100755
index ff75186..1d82fd4
--- a/res/drawable-hdpi/workspace_bg.9.png
+++ b/res/drawable-hdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-land-hdpi/workspace_bg.9.png b/res/drawable-land-hdpi/workspace_bg.9.png
deleted file mode 100644
index eecd6de..0000000
--- a/res/drawable-land-hdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-mdpi/workspace_bg.9.png b/res/drawable-land-mdpi/workspace_bg.9.png
deleted file mode 100644
index 626f4a4..0000000
--- a/res/drawable-land-mdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-xhdpi/workspace_bg.9.png b/res/drawable-land-xhdpi/workspace_bg.9.png
deleted file mode 100644
index 60f7d73..0000000
--- a/res/drawable-land-xhdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-xxhdpi/workspace_bg.9.png b/res/drawable-land-xxhdpi/workspace_bg.9.png
deleted file mode 100644
index fc71a0f..0000000
--- a/res/drawable-land-xxhdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-ldrtl/container_fastscroll_popup_bg.xml b/res/drawable-ldrtl/container_fastscroll_popup_bg.xml
index d790968..2bbf5cd 100644
--- a/res/drawable-ldrtl/container_fastscroll_popup_bg.xml
+++ b/res/drawable-ldrtl/container_fastscroll_popup_bg.xml
@@ -16,7 +16,7 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="@color/container_fastscroll_thumb_active_color" />
+    <solid android:color="?android:attr/colorAccent" />
     <size
         android:width="64dp"
         android:height="64dp" />
diff --git a/res/drawable-mdpi/cling_bg.9.png b/res/drawable-mdpi/cling_bg.9.png
deleted file mode 100644
index 6384f29..0000000
--- a/res/drawable-mdpi/cling_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_hand.png b/res/drawable-mdpi/ic_all_apps_bg_hand.png
index 8868d6b..0d1d7bb 100644
--- a/res/drawable-mdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-mdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_icon_1.png b/res/drawable-mdpi/ic_all_apps_bg_icon_1.png
deleted file mode 100644
index 4c78288..0000000
--- a/res/drawable-mdpi/ic_all_apps_bg_icon_1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_icon_2.png b/res/drawable-mdpi/ic_all_apps_bg_icon_2.png
deleted file mode 100644
index 0ed311b..0000000
--- a/res/drawable-mdpi/ic_all_apps_bg_icon_2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_icon_3.png b/res/drawable-mdpi/ic_all_apps_bg_icon_3.png
deleted file mode 100644
index 2aa3d4e..0000000
--- a/res/drawable-mdpi/ic_all_apps_bg_icon_3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_icon_4.png b/res/drawable-mdpi/ic_all_apps_bg_icon_4.png
deleted file mode 100644
index 2cdea9c..0000000
--- a/res/drawable-mdpi/ic_all_apps_bg_icon_4.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_arrow_back_grey.png b/res/drawable-mdpi/ic_arrow_back_grey.png
deleted file mode 100755
index 97999af..0000000
--- a/res/drawable-mdpi/ic_arrow_back_grey.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_add.png b/res/drawable-mdpi/ic_pageindicator_add.png
deleted file mode 100644
index d9939b4..0000000
--- a/res/drawable-mdpi/ic_pageindicator_add.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_current.png b/res/drawable-mdpi/ic_pageindicator_current.png
deleted file mode 100644
index 832f8ef..0000000
--- a/res/drawable-mdpi/ic_pageindicator_current.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_current_folder.png b/res/drawable-mdpi/ic_pageindicator_current_folder.png
deleted file mode 100644
index b6c4d7f..0000000
--- a/res/drawable-mdpi/ic_pageindicator_current_folder.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_default.png b/res/drawable-mdpi/ic_pageindicator_default.png
deleted file mode 100644
index 9c44afc..0000000
--- a/res/drawable-mdpi/ic_pageindicator_default.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_default_folder.png b/res/drawable-mdpi/ic_pageindicator_default_folder.png
deleted file mode 100644
index f462558..0000000
--- a/res/drawable-mdpi/ic_pageindicator_default_folder.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_search_grey.png b/res/drawable-mdpi/ic_search_grey.png
deleted file mode 100755
index c386dbb..0000000
--- a/res/drawable-mdpi/ic_search_grey.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_setting.png b/res/drawable-mdpi/ic_setting.png
deleted file mode 100644
index 60c4fa5..0000000
--- a/res/drawable-mdpi/ic_setting.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_setting_pressed.png b/res/drawable-mdpi/ic_setting_pressed.png
deleted file mode 100644
index 018bea3..0000000
--- a/res/drawable-mdpi/ic_setting_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_wallpaper.png b/res/drawable-mdpi/ic_wallpaper.png
deleted file mode 100644
index a934783..0000000
--- a/res/drawable-mdpi/ic_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_wallpaper_pressed.png b/res/drawable-mdpi/ic_wallpaper_pressed.png
deleted file mode 100644
index 08794d9..0000000
--- a/res/drawable-mdpi/ic_wallpaper_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_widget.png b/res/drawable-mdpi/ic_widget.png
deleted file mode 100644
index 5545350..0000000
--- a/res/drawable-mdpi/ic_widget.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_widget_pressed.png b/res/drawable-mdpi/ic_widget_pressed.png
deleted file mode 100644
index 634b415..0000000
--- a/res/drawable-mdpi/ic_widget_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/portal_ring_inner.png b/res/drawable-mdpi/portal_ring_inner.png
deleted file mode 100644
index 7c5e2b7..0000000
--- a/res/drawable-mdpi/portal_ring_inner.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/portal_ring_inner_nolip.png b/res/drawable-mdpi/portal_ring_inner_nolip.png
deleted file mode 100644
index 6ccdebb..0000000
--- a/res/drawable-mdpi/portal_ring_inner_nolip.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/portal_ring_outer.png b/res/drawable-mdpi/portal_ring_outer.png
deleted file mode 100644
index 40a73ab..0000000
--- a/res/drawable-mdpi/portal_ring_outer.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/portal_ring_rest.png b/res/drawable-mdpi/portal_ring_rest.png
deleted file mode 100644
index b2c733b..0000000
--- a/res/drawable-mdpi/portal_ring_rest.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/workspace_bg.9.png b/res/drawable-mdpi/workspace_bg.9.png
old mode 100644
new mode 100755
index c67c432..116ce44
--- a/res/drawable-mdpi/workspace_bg.9.png
+++ b/res/drawable-mdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-nodpi/ic_migration.png b/res/drawable-nodpi/ic_migration.png
deleted file mode 100644
index 14f8721..0000000
--- a/res/drawable-nodpi/ic_migration.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw720dp-hdpi/workspace_bg.9.png b/res/drawable-sw720dp-hdpi/workspace_bg.9.png
deleted file mode 100644
index ff75186..0000000
--- a/res/drawable-sw720dp-hdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw720dp-mdpi/workspace_bg.9.png b/res/drawable-sw720dp-mdpi/workspace_bg.9.png
deleted file mode 100644
index c67c432..0000000
--- a/res/drawable-sw720dp-mdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw720dp-xhdpi/workspace_bg.9.png b/res/drawable-sw720dp-xhdpi/workspace_bg.9.png
deleted file mode 100644
index 0b80cbf..0000000
--- a/res/drawable-sw720dp-xhdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw720dp-xxhdpi/workspace_bg.9.png b/res/drawable-sw720dp-xxhdpi/workspace_bg.9.png
deleted file mode 100644
index 0d180c2..0000000
--- a/res/drawable-sw720dp-xxhdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-v21/all_apps_search_market_bg.xml b/res/drawable-v21/all_apps_search_market_bg.xml
deleted file mode 100644
index 7bd2f88..0000000
--- a/res/drawable-v21/all_apps_search_market_bg.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/all_apps_search_market_button_focused_bg_color">
-    <item android:drawable="@color/quantum_panel_bg_color" />
-</ripple>
diff --git a/res/drawable-xhdpi/cling_bg.9.png b/res/drawable-xhdpi/cling_bg.9.png
deleted file mode 100644
index 7d8b1f0..0000000
--- a/res/drawable-xhdpi/cling_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_hand.png b/res/drawable-xhdpi/ic_all_apps_bg_hand.png
index 8a67245..e727d37 100644
--- a/res/drawable-xhdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-xhdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_icon_1.png b/res/drawable-xhdpi/ic_all_apps_bg_icon_1.png
deleted file mode 100644
index c0ebaed..0000000
--- a/res/drawable-xhdpi/ic_all_apps_bg_icon_1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_icon_2.png b/res/drawable-xhdpi/ic_all_apps_bg_icon_2.png
deleted file mode 100644
index 71cf250..0000000
--- a/res/drawable-xhdpi/ic_all_apps_bg_icon_2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_icon_3.png b/res/drawable-xhdpi/ic_all_apps_bg_icon_3.png
deleted file mode 100644
index 3c69fc5..0000000
--- a/res/drawable-xhdpi/ic_all_apps_bg_icon_3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_icon_4.png b/res/drawable-xhdpi/ic_all_apps_bg_icon_4.png
deleted file mode 100644
index 5f6ca38..0000000
--- a/res/drawable-xhdpi/ic_all_apps_bg_icon_4.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_arrow_back_grey.png b/res/drawable-xhdpi/ic_arrow_back_grey.png
deleted file mode 100755
index 22854bf..0000000
--- a/res/drawable-xhdpi/ic_arrow_back_grey.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_add.png b/res/drawable-xhdpi/ic_pageindicator_add.png
deleted file mode 100644
index 7e18c05..0000000
--- a/res/drawable-xhdpi/ic_pageindicator_add.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_current.png b/res/drawable-xhdpi/ic_pageindicator_current.png
deleted file mode 100644
index 866725f..0000000
--- a/res/drawable-xhdpi/ic_pageindicator_current.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_current_folder.png b/res/drawable-xhdpi/ic_pageindicator_current_folder.png
deleted file mode 100644
index ec19b7c..0000000
--- a/res/drawable-xhdpi/ic_pageindicator_current_folder.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_default.png b/res/drawable-xhdpi/ic_pageindicator_default.png
deleted file mode 100644
index 0cde8f4..0000000
--- a/res/drawable-xhdpi/ic_pageindicator_default.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_default_folder.png b/res/drawable-xhdpi/ic_pageindicator_default_folder.png
deleted file mode 100644
index 7c22d41..0000000
--- a/res/drawable-xhdpi/ic_pageindicator_default_folder.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_search_grey.png b/res/drawable-xhdpi/ic_search_grey.png
deleted file mode 100755
index e63182d..0000000
--- a/res/drawable-xhdpi/ic_search_grey.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_setting.png b/res/drawable-xhdpi/ic_setting.png
deleted file mode 100644
index bb90789..0000000
--- a/res/drawable-xhdpi/ic_setting.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_setting_pressed.png b/res/drawable-xhdpi/ic_setting_pressed.png
deleted file mode 100644
index 949373f..0000000
--- a/res/drawable-xhdpi/ic_setting_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_wallpaper.png b/res/drawable-xhdpi/ic_wallpaper.png
deleted file mode 100644
index 0acf773..0000000
--- a/res/drawable-xhdpi/ic_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_wallpaper_pressed.png b/res/drawable-xhdpi/ic_wallpaper_pressed.png
deleted file mode 100644
index e1e291d..0000000
--- a/res/drawable-xhdpi/ic_wallpaper_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_widget.png b/res/drawable-xhdpi/ic_widget.png
deleted file mode 100644
index 94bb79f..0000000
--- a/res/drawable-xhdpi/ic_widget.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_widget_pressed.png b/res/drawable-xhdpi/ic_widget_pressed.png
deleted file mode 100644
index 1dcaf37..0000000
--- a/res/drawable-xhdpi/ic_widget_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/portal_ring_inner.png b/res/drawable-xhdpi/portal_ring_inner.png
deleted file mode 100644
index b088042..0000000
--- a/res/drawable-xhdpi/portal_ring_inner.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/portal_ring_inner_nolip.png b/res/drawable-xhdpi/portal_ring_inner_nolip.png
deleted file mode 100644
index decf766..0000000
--- a/res/drawable-xhdpi/portal_ring_inner_nolip.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/portal_ring_outer.png b/res/drawable-xhdpi/portal_ring_outer.png
deleted file mode 100644
index 5ab9a21..0000000
--- a/res/drawable-xhdpi/portal_ring_outer.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/portal_ring_rest.png b/res/drawable-xhdpi/portal_ring_rest.png
deleted file mode 100644
index 7d1c842..0000000
--- a/res/drawable-xhdpi/portal_ring_rest.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/workspace_bg.9.png b/res/drawable-xhdpi/workspace_bg.9.png
old mode 100644
new mode 100755
index 0b80cbf..b1b3b85
--- a/res/drawable-xhdpi/workspace_bg.9.png
+++ b/res/drawable-xhdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/cling_bg.9.png b/res/drawable-xxhdpi/cling_bg.9.png
deleted file mode 100644
index d31ea70..0000000
--- a/res/drawable-xxhdpi/cling_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_hand.png b/res/drawable-xxhdpi/ic_all_apps_bg_hand.png
index ed694f8..fffcc6b 100644
--- a/res/drawable-xxhdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-xxhdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_icon_1.png b/res/drawable-xxhdpi/ic_all_apps_bg_icon_1.png
deleted file mode 100644
index 5cb0427..0000000
--- a/res/drawable-xxhdpi/ic_all_apps_bg_icon_1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_icon_2.png b/res/drawable-xxhdpi/ic_all_apps_bg_icon_2.png
deleted file mode 100644
index cd0322b..0000000
--- a/res/drawable-xxhdpi/ic_all_apps_bg_icon_2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_icon_3.png b/res/drawable-xxhdpi/ic_all_apps_bg_icon_3.png
deleted file mode 100644
index 19ffc2d..0000000
--- a/res/drawable-xxhdpi/ic_all_apps_bg_icon_3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_icon_4.png b/res/drawable-xxhdpi/ic_all_apps_bg_icon_4.png
deleted file mode 100644
index 311c3df..0000000
--- a/res/drawable-xxhdpi/ic_all_apps_bg_icon_4.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_arrow_back_grey.png b/res/drawable-xxhdpi/ic_arrow_back_grey.png
deleted file mode 100755
index a3ed052..0000000
--- a/res/drawable-xxhdpi/ic_arrow_back_grey.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_add.png b/res/drawable-xxhdpi/ic_pageindicator_add.png
deleted file mode 100644
index d790e86..0000000
--- a/res/drawable-xxhdpi/ic_pageindicator_add.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_current.png b/res/drawable-xxhdpi/ic_pageindicator_current.png
deleted file mode 100644
index 9550c61..0000000
--- a/res/drawable-xxhdpi/ic_pageindicator_current.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_current_folder.png b/res/drawable-xxhdpi/ic_pageindicator_current_folder.png
deleted file mode 100644
index 987c882..0000000
--- a/res/drawable-xxhdpi/ic_pageindicator_current_folder.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_default.png b/res/drawable-xxhdpi/ic_pageindicator_default.png
deleted file mode 100644
index 3bee96f..0000000
--- a/res/drawable-xxhdpi/ic_pageindicator_default.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_default_folder.png b/res/drawable-xxhdpi/ic_pageindicator_default_folder.png
deleted file mode 100644
index 46ff473..0000000
--- a/res/drawable-xxhdpi/ic_pageindicator_default_folder.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_search_grey.png b/res/drawable-xxhdpi/ic_search_grey.png
deleted file mode 100755
index 33b4ea9..0000000
--- a/res/drawable-xxhdpi/ic_search_grey.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_setting.png b/res/drawable-xxhdpi/ic_setting.png
deleted file mode 100644
index 3effb50..0000000
--- a/res/drawable-xxhdpi/ic_setting.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_setting_pressed.png b/res/drawable-xxhdpi/ic_setting_pressed.png
deleted file mode 100644
index d78cad6..0000000
--- a/res/drawable-xxhdpi/ic_setting_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_wallpaper.png b/res/drawable-xxhdpi/ic_wallpaper.png
deleted file mode 100644
index 218fd1d..0000000
--- a/res/drawable-xxhdpi/ic_wallpaper.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_wallpaper_pressed.png b/res/drawable-xxhdpi/ic_wallpaper_pressed.png
deleted file mode 100644
index 52c92cb..0000000
--- a/res/drawable-xxhdpi/ic_wallpaper_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_widget.png b/res/drawable-xxhdpi/ic_widget.png
deleted file mode 100644
index cc5002e..0000000
--- a/res/drawable-xxhdpi/ic_widget.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_widget_pressed.png b/res/drawable-xxhdpi/ic_widget_pressed.png
deleted file mode 100644
index 0c9b02a..0000000
--- a/res/drawable-xxhdpi/ic_widget_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/portal_ring_inner.png b/res/drawable-xxhdpi/portal_ring_inner.png
deleted file mode 100644
index cd23cf7..0000000
--- a/res/drawable-xxhdpi/portal_ring_inner.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/portal_ring_inner_nolip.png b/res/drawable-xxhdpi/portal_ring_inner_nolip.png
deleted file mode 100644
index d82b910..0000000
--- a/res/drawable-xxhdpi/portal_ring_inner_nolip.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/portal_ring_outer.png b/res/drawable-xxhdpi/portal_ring_outer.png
deleted file mode 100644
index e5d33b2..0000000
--- a/res/drawable-xxhdpi/portal_ring_outer.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/portal_ring_rest.png b/res/drawable-xxhdpi/portal_ring_rest.png
deleted file mode 100644
index d52825c..0000000
--- a/res/drawable-xxhdpi/portal_ring_rest.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/workspace_bg.9.png b/res/drawable-xxhdpi/workspace_bg.9.png
old mode 100644
new mode 100755
index 0d180c2..d47f6b2
--- a/res/drawable-xxhdpi/workspace_bg.9.png
+++ b/res/drawable-xxhdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png b/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png
index 615374a..4d065d8 100644
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_1.png b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_1.png
deleted file mode 100644
index 10f8c41..0000000
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_2.png b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_2.png
deleted file mode 100644
index 102d925..0000000
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_3.png b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_3.png
deleted file mode 100644
index 9be5b7a..0000000
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_4.png b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_4.png
deleted file mode 100644
index d7fb29b..0000000
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_4.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_arrow_back_grey.png b/res/drawable-xxxhdpi/ic_arrow_back_grey.png
deleted file mode 100755
index 6b42051..0000000
--- a/res/drawable-xxxhdpi/ic_arrow_back_grey.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_search_grey.png b/res/drawable-xxxhdpi/ic_search_grey.png
deleted file mode 100755
index d957186..0000000
--- a/res/drawable-xxxhdpi/ic_search_grey.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/portal_ring_inner.png b/res/drawable-xxxhdpi/portal_ring_inner.png
deleted file mode 100644
index 59e811d..0000000
--- a/res/drawable-xxxhdpi/portal_ring_inner.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/portal_ring_inner_nolip.png b/res/drawable-xxxhdpi/portal_ring_inner_nolip.png
deleted file mode 100644
index c1e7585..0000000
--- a/res/drawable-xxxhdpi/portal_ring_inner_nolip.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/portal_ring_outer.png b/res/drawable-xxxhdpi/portal_ring_outer.png
deleted file mode 100644
index f2f818b..0000000
--- a/res/drawable-xxxhdpi/portal_ring_outer.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/portal_ring_rest.png b/res/drawable-xxxhdpi/portal_ring_rest.png
deleted file mode 100644
index 2af67b8..0000000
--- a/res/drawable-xxxhdpi/portal_ring_rest.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/workspace_bg.9.png b/res/drawable-xxxhdpi/workspace_bg.9.png
new file mode 100755
index 0000000..3281548
--- /dev/null
+++ b/res/drawable-xxxhdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable/horizontal_line.xml b/res/drawable/all_apps_divider.xml
similarity index 82%
rename from res/drawable/horizontal_line.xml
rename to res/drawable/all_apps_divider.xml
index 3f3f17e3..3fe5295 100644
--- a/res/drawable/horizontal_line.xml
+++ b/res/drawable/all_apps_divider.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2016 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,7 +14,7 @@
      limitations under the License.
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
+       android:shape="rectangle">
+    <solid android:color="@color/all_apps_divider_color" />
     <size android:height="1dp" />
-    <solid android:color="#ddd" />
-</shape>
+</shape>
\ No newline at end of file
diff --git a/res/drawable/all_apps_search_bg.xml b/res/drawable/all_apps_search_bg.xml
deleted file mode 100644
index 5a2c9e8..0000000
--- a/res/drawable/all_apps_search_bg.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 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.
--->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <shape android:shape="rectangle">
-            <solid android:color="@color/quantum_panel_bg_color" />
-            <corners
-                android:topLeftRadius="2dp"
-                android:topRightRadius="2dp" />
-        </shape>
-    </item>
-    <item
-        android:top="@dimen/all_apps_search_bar_bg_overflow"
-        android:left="@dimen/all_apps_search_bar_bg_overflow"
-        android:right="@dimen/all_apps_search_bar_bg_overflow"
-        android:bottom="0dp">
-
-        <shape android:shape="rectangle">
-            <stroke
-                android:width="@dimen/all_apps_search_bar_divider_width"
-                android:color="#1E000000"/>
-        </shape>
-    </item>
-</layer-list>
\ No newline at end of file
diff --git a/res/drawable/horizontal_line.xml b/res/drawable/all_apps_search_divider.xml
similarity index 82%
copy from res/drawable/horizontal_line.xml
copy to res/drawable/all_apps_search_divider.xml
index 3f3f17e3..99905e4 100644
--- a/res/drawable/horizontal_line.xml
+++ b/res/drawable/all_apps_search_divider.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2016 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,7 +14,7 @@
      limitations under the License.
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
+       android:shape="rectangle">
+    <solid android:color="?android:attr/colorAccent" />
     <size android:height="1dp" />
-    <solid android:color="#ddd" />
-</shape>
+</shape>
\ No newline at end of file
diff --git a/res/drawable/focusable_view_bg.xml b/res/drawable/all_apps_search_hint.xml
similarity index 74%
rename from res/drawable/focusable_view_bg.xml
rename to res/drawable/all_apps_search_hint.xml
index e156513..b2ff7a4 100644
--- a/res/drawable/focusable_view_bg.xml
+++ b/res/drawable/all_apps_search_hint.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2011 The Android Open Source Project
+     Copyright (C) 2016 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.
@@ -14,13 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <item android:state_focused="true">
-        <shape android:shape="rectangle">
-            <solid android:color="@color/focused_background" />
-        </shape>
-    </item>
-
+    <item android:color="@android:color/transparent" android:state_focused="true" />
+    <item android:color="?android:attr/colorAccent"/>
 </selector>
\ No newline at end of file
diff --git a/res/drawable/all_apps_search_market_bg.xml b/res/drawable/all_apps_search_market_bg.xml
deleted file mode 100644
index 5278e00..0000000
--- a/res/drawable/all_apps_search_market_bg.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true" android:drawable="@color/all_apps_search_market_button_focused_bg_color" />
-    <item android:state_pressed="true" android:drawable="@color/all_apps_search_market_button_focused_bg_color" />
-    <item android:drawable="@android:color/transparent" />
-</selector>
diff --git a/res/drawable/bg_celllayout.xml b/res/drawable/bg_celllayout.xml
new file mode 100644
index 0000000..d2219b3
--- /dev/null
+++ b/res/drawable/bg_celllayout.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<transition xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
+            <solid android:color="@color/spring_loaded_panel_color" />
+        </shape>
+    </item>
+    <item>
+        <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
+            <stroke
+                android:width="@dimen/spring_loaded_panel_border"
+                android:color="@color/spring_loaded_highlighted_panel_border_color" />
+            <solid android:color="@android:color/transparent" />
+        </shape>
+    </item>
+
+</transition>
\ No newline at end of file
diff --git a/res/drawable/bg_migration_cling.xml b/res/drawable/bg_migration_cling.xml
deleted file mode 100644
index bfff5a4..0000000
--- a/res/drawable/bg_migration_cling.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="oval" >
-
-    <gradient
-        android:endColor="#00ffeb3a"
-        android:gradientRadius="50%p"
-        android:startColor="#80ffeb3a"
-        android:type="radial" />
-
-</shape>
\ No newline at end of file
diff --git a/res/drawable/focusable_view_bg.xml b/res/drawable/bg_pill_focused.xml
similarity index 71%
copy from res/drawable/focusable_view_bg.xml
copy to res/drawable/bg_pill_focused.xml
index e156513..37afad0 100644
--- a/res/drawable/focusable_view_bg.xml
+++ b/res/drawable/bg_pill_focused.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2011 The Android Open Source Project
+<!-- Copyright (C) 2016 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.
@@ -16,11 +15,11 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-
     <item android:state_focused="true">
-        <shape android:shape="rectangle">
-            <solid android:color="@color/focused_background" />
+        <shape xmlns:android="http://schemas.android.com/apk/res/android"
+               android:shape="rectangle">
+            <stroke android:color="#616161" android:width="2dp"/>
+            <corners android:radius="@dimen/bg_pill_radius" />
         </shape>
     </item>
-
 </selector>
\ No newline at end of file
diff --git a/res/drawable/bg_screenpanel.xml b/res/drawable/bg_screenpanel.xml
index cdb71df..346fca0 100644
--- a/res/drawable/bg_screenpanel.xml
+++ b/res/drawable/bg_screenpanel.xml
@@ -18,6 +18,7 @@
 */
 -->
 
+<!-- TODO(twickham): Remove this file and the screenpanel drawables -->
 <transition xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <item android:drawable="@drawable/screenpanel"/>
diff --git a/res/drawable/horizontal_line.xml b/res/drawable/bg_white_pill.xml
similarity index 78%
copy from res/drawable/horizontal_line.xml
copy to res/drawable/bg_white_pill.xml
index 3f3f17e3..f92f739 100644
--- a/res/drawable/horizontal_line.xml
+++ b/res/drawable/bg_white_pill.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2016 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.
@@ -14,8 +13,9 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <size android:height="1dp" />
-    <solid android:color="#ddd" />
-</shape>
+       android:shape="rectangle">
+    <solid android:color="#FFFFFF" />
+    <corners android:radius="@dimen/bg_pill_radius" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/container_fastscroll_popup_bg.xml b/res/drawable/container_fastscroll_popup_bg.xml
index 2ef07ab..3dc7680 100644
--- a/res/drawable/container_fastscroll_popup_bg.xml
+++ b/res/drawable/container_fastscroll_popup_bg.xml
@@ -16,7 +16,7 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="@color/container_fastscroll_thumb_active_color" />
+    <solid android:color="?android:attr/colorAccent" />
     <size
         android:width="64dp"
         android:height="64dp" />
diff --git a/WallpaperPicker/res/drawable-v21/ic_tick.xml b/res/drawable/deep_shortcuts_drag_handle.xml
similarity index 63%
rename from WallpaperPicker/res/drawable-v21/ic_tick.xml
rename to res/drawable/deep_shortcuts_drag_handle.xml
index 5b27027..99d2b07 100644
--- a/WallpaperPicker/res/drawable-v21/ic_tick.xml
+++ b/res/drawable/deep_shortcuts_drag_handle.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
+<!-- Copyright (C) 2016 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.
@@ -16,16 +15,12 @@
 -->
 
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:height="48dp"
-    android:viewportHeight="48"
-    android:viewportWidth="48"
-    android:width="48dp" >
+        android:width="@dimen/deep_shortcut_drag_handle_size"
+        android:height="@dimen/deep_shortcut_drag_handle_size"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
 
-    <group>
-        <path
-            android:name="tick"
-            android:fillColor="#FFFFFFFF"
-            android:pathData="M18 32.34l-8.34-8.34-2.83 2.83 11.17 11.17 24-24-2.83-2.83z" />
-    </group>
-
+    <path
+        android:pathData="M20 9H4v2h16V9zM4 15h16v-2H4v2z"
+        android:fillColor="#4D000000"/>
 </vector>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_icon_1.xml b/res/drawable/ic_all_apps_bg_icon_1.xml
new file mode 100644
index 0000000..c9c0a75
--- /dev/null
+++ b/res/drawable/ic_all_apps_bg_icon_1.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+
+    <path
+        android:fillColor="#1A000000"
+        android:pathData="M44.28,30.96c4.84-10.68,0.09-23.27-10.59-28.11S10.42,2.74,5.58,13.42
+        C1,23.54,6.5,35.92,16.62,40.51l0,0l-3.23,7.12C27.84,47,39.79,40.86,44.28,30.96z" />
+    <path
+        android:fillColor="#E0E0E0"
+        android:pathData="M41.75,30.05c4.84-10.68,0.09-23.27-10.59-28.11S7.9,1.83,3.06,12.51
+        c-4.59,10.12,0.92,22.5,11.03,27.09l0,0l-3.23,7.12C25.31,46.09,37.26,39.94,41.75,30.05z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_icon_2.xml b/res/drawable/ic_all_apps_bg_icon_2.xml
new file mode 100644
index 0000000..b6269e3
--- /dev/null
+++ b/res/drawable/ic_all_apps_bg_icon_2.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+
+    <path
+        android:fillColor="#1A000000"
+        android:pathData="M20.54,44.59c0.57-0.04,1.15-0.38,1.67-1.04l24.23-30.62c0.62-0.78,0.77-1.54,0.52-2.12
+        c-0.25-0.58-0.9-0.99-1.89-1.1L6.2,5.99C5.39,5.91,4.74,6.08,4.32,6.44l0,0C3.7,6.97,3.55,7.88,4.01,8.96l14.54,34.09
+        C19,44.13,19.75,44.65,20.54,44.59L20.54,44.59z" />
+    <path
+        android:fillColor="#E0E0E0"
+        android:pathData="M18.49,43.22c0.57-0.04,1.15-0.38,1.67-1.04l24.23-30.62c0.62-0.78,0.77-1.54,0.52-2.12
+        c-0.25-0.58-0.9-0.99-1.89-1.1L4.15,4.62C3.34,4.54,2.69,4.71,2.27,5.08l0,0C1.65,5.6,1.5,6.52,1.96,7.6L16.5,41.69
+        C16.96,42.76,17.7,43.28,18.49,43.22L18.49,43.22z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_all_apps_bg_icon_3.xml b/res/drawable/ic_all_apps_bg_icon_3.xml
new file mode 100644
index 0000000..4c255a9
--- /dev/null
+++ b/res/drawable/ic_all_apps_bg_icon_3.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+
+    <path
+        android:fillColor="#1A000000"
+        android:pathData="M25.18,1.27c-12.32,0-23.41,9.99-23.41,22.31s11.09,22.31,23.41,22.31
+        s22.31-9.99,22.31-22.31S37.5,1.27,25.18,1.27z M25.18,33.55c-5.5,0-14.35-5.1-14.35-10.6s8.32-12.19,13.82-12.19
+        c5.5,0,10.49,7.33,10.49,12.83S30.68,33.55,25.18,33.55z" />
+    <path
+        android:fillColor="#E0E0E0"
+        android:pathData="M22.93,0.22c-12.32,0-22.31,9.99-22.31,22.31s9.99,22.31,22.31,22.31
+        s22.31-9.99,22.31-22.31S35.25,0.22,22.93,0.22z M22.93,32.5c-5.5,0-9.97-4.46-9.97-9.97s4.46-9.97,9.97-9.97
+        c5.5,0,9.97,4.46,9.97,9.97S28.43,32.5,22.93,32.5z" />
+    <path
+        android:fillColor="#E0E0E0"
+        android:pathData="M14.81,22.53a8.12,8.12 0 1,0 16.24,0a8.12,8.12 0 1,0 -16.24,0z" />
+</vector>
diff --git a/res/drawable/ic_all_apps_bg_icon_4.xml b/res/drawable/ic_all_apps_bg_icon_4.xml
new file mode 100644
index 0000000..12e05bc
--- /dev/null
+++ b/res/drawable/ic_all_apps_bg_icon_4.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+
+    <path
+        android:fillColor="#1A000000"
+        android:pathData="M11.53,8.02l23.39-5.73c1.61-0.39,3.25,0.6,3.64,2.21l7.64,31.19
+        c0.39,1.61-0.6,3.25-2.21,3.64L12.8,46.97c-1.61,0.39-3.25-0.6-3.64-2.21L3.43,21.37L11.53,8.02z" />
+    <path
+        android:fillColor="#E0E0E0"
+        android:pathData="M9.2,6.53L32.59,0.8C34.2,0.4,35.84,1.4,36.23,3l7.64,31.19c0.39,1.61-0.6,3.25-2.21,3.64
+        l-31.19,7.64c-1.61,0.39-3.25-0.6-3.64-2.21L1.11,19.87L9.2,6.53z" />
+    <path
+        android:fillColor="#1A000000"
+        android:pathData="M9.27,6.47l1.91,7.8c0.4,1.62-0.59,3.24-2.21,3.64l-7.8,1.91L9.27,6.47z" />
+</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_allapps_search.xml b/res/drawable/ic_allapps_search.xml
new file mode 100644
index 0000000..2aeb947
--- /dev/null
+++ b/res/drawable/ic_allapps_search.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+     Copyright (C) 2016 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0">
+    <path
+        android:fillColor="?android:attr/colorAccent"
+        android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
+</vector>
diff --git a/res/drawable/ic_setting.xml b/res/drawable/ic_setting.xml
new file mode 100644
index 0000000..8a50c0c
--- /dev/null
+++ b/res/drawable/ic_setting.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2016 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M38.86 25.95c.08-.64 .14-1.29 .14-1.95s-.06-1.31-.14-1.95l4.23-3.31c.38-.3 .49-.84 .24-1.28l-4-6.93c-.25-.43-.77-.61-1.22-.43l-4.98 2.01c-1.03-.79-2.16-1.46-3.38-1.97L29 4.84c-.09-.47-.5-.84-1-.84h-8c-.5 0-.91 .37-.99 .84l-.75 5.3c-1.22 .51-2.35 1.17-3.38 1.97L9.9 10.1c-.45-.17-.97 0-1.22 .43l-4 6.93c-.25 .43-.14 .97 .24 1.28l4.22 3.31C9.06 22.69 9 23.34 9 24s.06 1.31 .14 1.95l-4.22 3.31c-.38 .3-.49 .84-.24 1.28l4 6.93c.25 .43 .77 .61 1.22 .43l4.98-2.01c1.03 .79 2.16 1.46 3.38 1.97l.75 5.3c.08 .47 .49 .84 .99 .84h8c.5 0 .91-.37 .99-.84l.75-5.3c1.22-.51 2.35-1.17 3.38-1.97l4.98 2.01c.45 .17 .97 0 1.22-.43l4-6.93c.25-.43 .14-.97-.24-1.28l-4.22-3.31zM24 31c-3.87 0-7-3.13-7-7s3.13-7 7-7 7 3.13 7 7-3.13 7-7 7z"/>
+</vector>
diff --git a/res/drawable/ic_wallpaper.xml b/res/drawable/ic_wallpaper.xml
new file mode 100644
index 0000000..7af4b2a
--- /dev/null
+++ b/res/drawable/ic_wallpaper.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2016 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M8 8h14V4H8C5.79 4 4 5.79 4 8v14h4V8zm12 18l-8 10h24l-6-8-4.06 5.42L20 26zm14-9c0-1.66-1.34-3-3-3s-3 1.34-3 3 1.34 3 3 3 3-1.34 3-3zm6-13H26v4h14v14h4V8c0-2.21-1.79-4-4-4zm0 36H26v4h14c2.21 0 4-1.79 4-4V26h-4v14zM8 26H4v14c0 2.21 1.79 4 4 4h14v-4H8V26z"/>
+</vector>
diff --git a/res/drawable/ic_widget.xml b/res/drawable/ic_widget.xml
new file mode 100644
index 0000000..3e7bd7b
--- /dev/null
+++ b/res/drawable/ic_widget.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2016 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M26 26v16h16V26H26zM6 42h16V26H6v16zM6 6v16h16V6H6zm27.31-2.63L22 14.69 33.31 26l11.31-11.31L33.31 3.37z"/>
+</vector>
diff --git a/res/drawable/quantum_panel_shape_dark.xml b/res/drawable/quantum_panel_shape_dark.xml
index c3821c4..b299eb8 100644
--- a/res/drawable/quantum_panel_shape_dark.xml
+++ b/res/drawable/quantum_panel_shape_dark.xml
@@ -16,7 +16,7 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="@color/quantum_panel_bg_color_dark" />
+    <solid android:color="?attr/colorSecondary" />
     <corners
         android:radius="2dp" />
-</shape>
\ No newline at end of file
+</shape>
diff --git a/res/drawable/setting_button.xml b/res/drawable/setting_button.xml
deleted file mode 100644
index 4d66a1a..0000000
--- a/res/drawable/setting_button.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true" android:drawable="@drawable/ic_setting_pressed" />
-    <item android:state_pressed="true" android:drawable="@drawable/ic_setting_pressed" />
-    <item android:drawable="@drawable/ic_setting" />
-</selector>
diff --git a/res/drawable/wallpaper_button.xml b/res/drawable/wallpaper_button.xml
deleted file mode 100644
index 72da99d..0000000
--- a/res/drawable/wallpaper_button.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true" android:drawable="@drawable/ic_wallpaper_pressed" />
-    <item android:state_pressed="true" android:drawable="@drawable/ic_wallpaper_pressed" />
-    <item android:drawable="@drawable/ic_wallpaper" />
-</selector>
diff --git a/res/drawable/widget_button.xml b/res/drawable/widget_button.xml
deleted file mode 100644
index 6936c87..0000000
--- a/res/drawable/widget_button.xml
+++ /dev/null
@@ -1,21 +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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true" android:drawable="@drawable/ic_widget_pressed" />
-    <item android:state_pressed="true" android:drawable="@drawable/ic_widget_pressed" />
-    <item android:drawable="@drawable/ic_widget" />
-</selector>
diff --git a/res/drawable/widgets_row_divider.xml b/res/drawable/widgets_row_divider.xml
index bb5b6b5..2c3c7a2 100644
--- a/res/drawable/widgets_row_divider.xml
+++ b/res/drawable/widgets_row_divider.xml
@@ -15,5 +15,5 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android" >
     <size android:width="@dimen/widget_row_divider" />
-    <solid android:color="@color/quantum_panel_bg_color_dark" />
+    <solid android:color="?attr/colorSecondary" />
 </shape>
diff --git a/res/interpolator/disco_bounce_section1.xml b/res/interpolator/disco_bounce_section1.xml
new file mode 100644
index 0000000..2156216
--- /dev/null
+++ b/res/interpolator/disco_bounce_section1.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+                  android:controlX1="0.9"
+                  android:controlY1="0"
+                  android:controlX2="0.5"
+                  android:controlY2="1"/>
diff --git a/res/interpolator/disco_bounce_section2.xml b/res/interpolator/disco_bounce_section2.xml
new file mode 100644
index 0000000..86cc789
--- /dev/null
+++ b/res/interpolator/disco_bounce_section2.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+                  android:controlX1="0.9"
+                  android:controlY1="0"
+                  android:controlX2="0.6"
+                  android:controlY2="1"/>
diff --git a/res/interpolator/disco_bounce_section3.xml b/res/interpolator/disco_bounce_section3.xml
new file mode 100644
index 0000000..1acef03
--- /dev/null
+++ b/res/interpolator/disco_bounce_section3.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 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
+  -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+                  android:controlX1="0.1"
+                  android:controlY1="0"
+                  android:controlX2="0.7"
+                  android:controlY2="1"/>
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index b1f85b4..ef0dfdc 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -23,18 +23,15 @@
     android:layout_height="match_parent"
     android:fitsSystemWindows="true">
 
-    <com.android.launcher3.DragLayer
+    <com.android.launcher3.dragndrop.DragLayer
         android:id="@+id/drag_layer"
         android:clipChildren="false"
         android:clipToPadding="false"
+        android:background="@drawable/workspace_bg"
+        android:importantForAccessibility="no"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-        <com.android.launcher3.FocusIndicatorView
-            android:id="@+id/focus_indicator"
-            android:layout_width="52dp"
-            android:layout_height="52dp" />
-
         <!-- The workspace contains 5 screens of cells -->
         <!-- DO NOT CHANGE THE ID -->
         <com.android.launcher3.Workspace
@@ -42,23 +39,36 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_gravity="center"
-            launcher:defaultScreen="@integer/config_workspaceDefaultScreen" />
+            launcher:pageIndicator="@id/page_indicator" />
 
         <!-- DO NOT CHANGE THE ID -->
         <include layout="@layout/hotseat"
             android:id="@+id/hotseat"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:layout_gravity="right" />
+            android:layout_gravity="right"
+            launcher:layout_ignoreInsets="true" />
 
         <include
-            android:id="@+id/search_drop_target_bar"
-            layout="@layout/search_drop_target_bar" />
+            android:id="@+id/drop_target_bar"
+            layout="@layout/drop_target_bar_vert" />
 
         <include layout="@layout/overview_panel"
             android:id="@+id/overview_panel"
             android:visibility="gone" />
 
+        <com.android.launcher3.pageindicators.PageIndicatorCaretLandscape
+            android:id="@+id/page_indicator"
+            android:layout_width="@dimen/dynamic_grid_page_indicator_height"
+            android:layout_height="@dimen/dynamic_grid_page_indicator_height"
+            android:layout_gravity="bottom|left"/>
+
+        <!-- A place holder view instead of the QSB in transposed layout -->
+        <View
+            android:layout_width="0dp"
+            android:layout_height="10dp"
+            android:id="@+id/workspace_blocked_row" />
+
         <include layout="@layout/widgets_view"
             android:id="@+id/widgets_view"
             android:layout_width="match_parent"
@@ -70,6 +80,7 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:visibility="invisible" />
-    </com.android.launcher3.DragLayer>
+
+    </com.android.launcher3.dragndrop.DragLayer>
 
 </com.android.launcher3.LauncherRootView>
diff --git a/res/layout-land/longpress_cling.xml b/res/layout-land/longpress_cling.xml
deleted file mode 100644
index 9672dd8..0000000
--- a/res/layout-land/longpress_cling.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/longpress_cling"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    launcher:layout_ignoreInsets="true"
-    android:background="@color/cling_scrim_background"
-    android:orientation="vertical" >
-
-    <Space
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1" />
-
-    <FrameLayout
-        android:id="@+id/cling_content"
-        android:layout_width="360dp"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:background="@drawable/cling_bg" />
-
-    <Space
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="2" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-land/migration_cling.xml b/res/layout-land/migration_cling.xml
deleted file mode 100644
index 269c1ae..0000000
--- a/res/layout-land/migration_cling.xml
+++ /dev/null
@@ -1,106 +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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/migration_cling"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    launcher:layout_ignoreInsets="true"
-    android:background="#FF009688"
-    android:baselineAligned="false"
-    android:gravity="center_vertical" >
-
-    <FrameLayout
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_weight="1" >
-
-        <ImageView
-            android:layout_width="@dimen/cling_migration_bg_size"
-            android:layout_height="@dimen/cling_migration_bg_size"
-            android:layout_gravity="center"
-            android:background="@drawable/bg_migration_cling" />
-
-        <ImageView
-            android:layout_width="@dimen/cling_migration_logo_width"
-            android:layout_height="@dimen/cling_migration_logo_height"
-            android:layout_gravity="center"
-            android:src="@drawable/ic_migration" />
-    </FrameLayout>
-
-    <LinearLayout
-        android:layout_width="@dimen/cling_migration_content_width"
-        android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/cling_migration_content_margin"
-        android:layout_marginRight="@dimen/cling_migration_content_margin"
-        android:orientation="vertical"
-        android:paddingLeft="24dp"
-        android:paddingRight="24dp" >
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:paddingBottom="8dp"
-            android:text="@string/first_run_cling_title"
-            android:textColor="#E1000000"
-            android:textSize="34sp" />
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:fontFamily="sans-serif-medium"
-            android:text="@string/migration_cling_title"
-            android:textColor="#E1000000"
-            android:textSize="20sp" />
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:paddingBottom="24dp"
-            android:text="@string/migration_cling_description"
-            android:textColor="#99000000"
-            android:textSize="16sp" />
-
-        <LinearLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" >
-
-            <Button
-                android:id="@+id/cling_dismiss_migration_copy_apps"
-                style="?android:attr/buttonBarButtonStyle"
-                android:layout_width="0dp"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:fontFamily="sans-serif-medium"
-                android:text="@string/migration_cling_copy_apps"
-                android:textColor="#FFFFFFFF"
-                android:textSize="14sp" />
-
-            <Button
-                android:id="@+id/cling_dismiss_migration_use_default"
-                style="?android:attr/buttonBarButtonStyle"
-                android:layout_width="0dp"
-                android:layout_height="wrap_content"
-                android:layout_weight="1"
-                android:fontFamily="sans-serif-medium"
-                android:text="@string/migration_cling_use_default"
-                android:textColor="#deFFFFFF"
-                android:textSize="14sp" />
-        </LinearLayout>
-    </LinearLayout>
-
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 962b5ce..a2e2f9b 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -24,25 +24,22 @@
     android:layout_height="match_parent"
     android:fitsSystemWindows="true">
 
-    <com.android.launcher3.DragLayer
+    <com.android.launcher3.dragndrop.DragLayer
         android:id="@+id/drag_layer"
         android:clipChildren="false"
+        android:importantForAccessibility="no"
         android:clipToPadding="false"
+        android:background="@drawable/workspace_bg"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-        <com.android.launcher3.FocusIndicatorView
-            android:id="@+id/focus_indicator"
-            android:layout_width="52dp"
-            android:layout_height="52dp" />
-
         <!-- The workspace contains 5 screens of cells -->
         <!-- DO NOT CHANGE THE ID -->
         <com.android.launcher3.Workspace
             android:id="@+id/workspace"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            launcher:defaultScreen="@integer/config_workspaceDefaultScreen"
+            android:layout_gravity="center"
             launcher:pageIndicator="@+id/page_indicator">
         </com.android.launcher3.Workspace>
 
@@ -50,7 +47,8 @@
         <include layout="@layout/hotseat"
             android:id="@+id/hotseat"
             android:layout_width="match_parent"
-            android:layout_height="match_parent" />
+            android:layout_height="match_parent"
+            launcher:layout_ignoreInsets="true" />
 
         <include layout="@layout/overview_panel"
             android:id="@+id/overview_panel"
@@ -58,16 +56,16 @@
 
         <!-- Keep these behind the workspace so that they are not visible when
              we go into AllApps -->
-        <include
-            android:id="@+id/page_indicator"
-            layout="@layout/page_indicator"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal" />
+        <include layout="@layout/page_indicator"
+            android:id="@+id/page_indicator" />
 
         <include
-            android:id="@+id/search_drop_target_bar"
-            layout="@layout/search_drop_target_bar" />
+            android:id="@+id/drop_target_bar"
+            layout="@layout/drop_target_bar_horz" />
+
+        <include
+            layout="@layout/qsb_container"
+            android:id="@+id/qsb_container" />
 
         <include layout="@layout/widgets_view"
             android:id="@+id/widgets_view"
@@ -79,7 +77,8 @@
             android:id="@+id/apps_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:visibility="invisible" />
-    </com.android.launcher3.DragLayer>
+            android:visibility="invisible"
+            launcher:layout_ignoreInsets="true" />
+    </com.android.launcher3.dragndrop.DragLayer>
 
 </com.android.launcher3.LauncherRootView>
diff --git a/res/layout-port/longpress_cling.xml b/res/layout-port/longpress_cling.xml
deleted file mode 100644
index c0b5267..0000000
--- a/res/layout-port/longpress_cling.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/longpress_cling"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    launcher:layout_ignoreInsets="true"
-    android:background="@color/cling_scrim_background" >
-
-    <FrameLayout
-        android:id="@+id/cling_content"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="top"
-        android:background="@drawable/cling_bg"
-        android:tag="crop_bg_top_and_sides" />
-
-</FrameLayout>
\ No newline at end of file
diff --git a/res/layout-port/migration_cling.xml b/res/layout-port/migration_cling.xml
deleted file mode 100644
index 3f696a2..0000000
--- a/res/layout-port/migration_cling.xml
+++ /dev/null
@@ -1,109 +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.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/migration_cling"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    launcher:layout_ignoreInsets="true"
-    android:background="#FF009688" >
-
-    <RelativeLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical" >
-
-        <ImageView
-            android:layout_width="@dimen/cling_migration_bg_size"
-            android:layout_height="@dimen/cling_migration_bg_size"
-            android:layout_below="@+id/ic_cling_migration"
-            android:layout_centerHorizontal="true"
-            android:layout_marginTop="@dimen/cling_migration_bg_shift"
-            android:src="@drawable/bg_migration_cling" />
-
-        <ImageView
-            android:id="@+id/ic_cling_migration"
-            android:layout_width="@dimen/cling_migration_logo_width"
-            android:layout_height="@dimen/cling_migration_logo_height"
-            android:layout_alignParentTop="true"
-            android:layout_centerHorizontal="true"
-            android:src="@drawable/ic_migration" />
-
-        <LinearLayout
-            android:layout_width="@dimen/cling_migration_content_width"
-            android:layout_height="wrap_content"
-            android:layout_below="@+id/ic_cling_migration"
-            android:layout_marginStart="@dimen/cling_migration_content_margin"
-            android:layout_marginLeft="@dimen/cling_migration_content_margin"
-            android:orientation="vertical"
-            android:paddingLeft="24dp"
-            android:paddingRight="24dp" >
-
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:paddingBottom="8dp"
-                android:text="@string/first_run_cling_title"
-                android:textColor="#E1000000"
-                android:textSize="34sp" />
-
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:fontFamily="sans-serif-medium"
-                android:text="@string/migration_cling_title"
-                android:textColor="#E1000000"
-                android:textSize="20sp" />
-
-            <TextView
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:paddingBottom="24dp"
-                android:text="@string/migration_cling_description"
-                android:textColor="#99000000"
-                android:textSize="16sp" />
-
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content" >
-
-                <Button
-                    android:id="@+id/cling_dismiss_migration_copy_apps"
-                    style="?android:attr/buttonBarButtonStyle"
-                    android:layout_width="0dp"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="1"
-                    android:fontFamily="sans-serif-medium"
-                    android:text="@string/migration_cling_copy_apps"
-                    android:textColor="#FFFFFFFF"
-                    android:textSize="14sp" />
-
-                <Button
-                    android:id="@+id/cling_dismiss_migration_use_default"
-                    style="?android:attr/buttonBarButtonStyle"
-                    android:layout_width="0dp"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="1"
-                    android:fontFamily="sans-serif-medium"
-                    android:text="@string/migration_cling_use_default"
-                    android:textColor="#deFFFFFF"
-                    android:textSize="14sp" />
-            </LinearLayout>
-        </LinearLayout>
-    </RelativeLayout>
-
-</FrameLayout>
\ No newline at end of file
diff --git a/res/layout-sw600dp-port/longpress_cling.xml b/res/layout-sw600dp-port/longpress_cling.xml
deleted file mode 100644
index c4573d5..0000000
--- a/res/layout-sw600dp-port/longpress_cling.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/longpress_cling"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    launcher:layout_ignoreInsets="true"
-    android:background="@color/cling_scrim_background"
-    android:orientation="vertical" >
-
-    <Space
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="1" />
-
-    <FrameLayout
-        android:id="@+id/cling_content"
-        android:layout_width="360dp"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:background="@drawable/cling_bg" />
-
-    <Space
-        android:layout_width="match_parent"
-        android:layout_height="0dp"
-        android:layout_weight="3" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 50daaa9..12c01b7 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -23,25 +23,22 @@
     android:layout_height="match_parent"
     android:fitsSystemWindows="true">
 
-    <com.android.launcher3.DragLayer
+    <com.android.launcher3.dragndrop.DragLayer
         android:id="@+id/drag_layer"
         android:clipChildren="false"
         android:clipToPadding="false"
+        android:importantForAccessibility="no"
+        android:background="@drawable/workspace_bg"
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-        <com.android.launcher3.FocusIndicatorView
-            android:id="@+id/focus_indicator"
-            android:layout_width="52dp"
-            android:layout_height="52dp" />
-
         <!-- The workspace contains 5 screens of cells -->
         <!-- DO NOT CHANGE THE ID -->
         <com.android.launcher3.Workspace
+            android:layout_gravity="center"
             android:id="@+id/workspace"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            launcher:defaultScreen="@integer/config_workspaceDefaultScreen"
             launcher:pageIndicator="@id/page_indicator">
         </com.android.launcher3.Workspace>
 
@@ -49,11 +46,12 @@
         <include layout="@layout/hotseat"
             android:id="@+id/hotseat"
             android:layout_width="match_parent"
-            android:layout_height="match_parent" />
+            android:layout_height="match_parent"
+            launcher:layout_ignoreInsets="true" />
 
         <include
-            android:id="@+id/search_drop_target_bar"
-            layout="@layout/search_drop_target_bar" />
+            android:id="@+id/drop_target_bar"
+            layout="@layout/drop_target_bar_horz" />
 
         <include layout="@layout/overview_panel"
             android:id="@+id/overview_panel"
@@ -61,11 +59,12 @@
 
         <!-- Keep these behind the workspace so that they are not visible when
              we go into AllApps -->
-        <include android:id="@+id/page_indicator"
-            layout="@layout/page_indicator"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal" />
+        <include layout="@layout/page_indicator"
+                 android:id="@+id/page_indicator" />
+
+        <include
+            layout="@layout/qsb_container"
+            android:id="@+id/qsb_container" />
 
         <include layout="@layout/widgets_view"
             android:id="@+id/widgets_view"
@@ -77,7 +76,8 @@
             android:id="@+id/apps_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:visibility="invisible" />
-    </com.android.launcher3.DragLayer>
+            android:visibility="invisible"
+           launcher:layout_ignoreInsets="true" />
+    </com.android.launcher3.dragndrop.DragLayer>
 
 </com.android.launcher3.LauncherRootView>
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index a677fff..4909eb3 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2016 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.
@@ -16,8 +16,7 @@
 <!-- The top and bottom paddings are defined in this container, but since we want
      the list view to span the full width (for touch interception purposes), we
      will bake the left/right padding into that view's background itself. -->
-<com.android.launcher3.allapps.AllAppsContainerView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.allapps.AllAppsContainerView xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:id="@+id/apps_view"
     android:layout_width="match_parent"
@@ -31,41 +30,40 @@
         android:layout_height="match_parent"
         android:layout_gravity="center"
         android:focusable="false"
-        android:elevation="2dp"
         android:visibility="invisible" />
 
 
     <com.android.launcher3.allapps.AllAppsRecyclerViewContainerView
+        android:id="@+id/main_content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:id="@+id/main_content"
-        android:saveEnabled="false"
-        android:visibility="gone"
         android:layout_gravity="center"
         android:focusable="true"
         android:focusableInTouchMode="true"
-        android:elevation="15dp" >
+        android:saveEnabled="false"
+        android:visibility="gone">
 
         <!-- DO NOT CHANGE THE ID -->
         <com.android.launcher3.allapps.AllAppsRecyclerView
             android:id="@+id/apps_list_view"
-            android:theme="@style/Theme.Light.CustomOverscroll"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_gravity="center_horizontal|top"
-            android:clipToPadding="false"
-            android:focusable="true"
             android:layout_marginTop="@dimen/all_apps_search_bar_height"
-            android:descendantFocusability="afterDescendants" />
+            android:clipToPadding="false"
+            android:descendantFocusability="afterDescendants"
+            android:focusable="true"
+            android:theme="@style/CustomOverscroll.Light" />
 
-        <LinearLayout
+        <FrameLayout
             android:id="@+id/search_container"
             android:layout_width="match_parent"
-            android:saveEnabled="false"
             android:layout_height="@dimen/all_apps_search_bar_height"
-            android:layout_gravity="start|top"
+            android:layout_gravity="center|top"
+            android:paddingTop="@dimen/all_apps_search_bar_margin_top"
+            android:gravity="center|bottom"
             android:orientation="horizontal"
-            android:background="@drawable/all_apps_search_bg" >
+            android:saveEnabled="false">
 
             <com.android.launcher3.ExtendedEditText
                 android:id="@+id/search_box_input"
@@ -73,19 +71,25 @@
                 android:layout_height="match_parent"
                 android:background="@android:color/transparent"
                 android:focusableInTouchMode="true"
-                android:gravity="fill_horizontal|center_vertical"
-                android:hint="@string/all_apps_search_bar_hint"
-                android:inputType="text|textNoSuggestions|textCapWords"
+                android:gravity="center"
                 android:imeOptions="actionSearch|flagNoExtractUi"
+                android:inputType="text|textNoSuggestions|textCapWords"
                 android:maxLines="1"
                 android:scrollHorizontally="true"
-                android:layout_marginLeft="@dimen/container_fastscroll_thumb_max_width"
-                android:layout_marginRight="@dimen/container_fastscroll_thumb_max_width"
                 android:singleLine="true"
                 android:textColor="#4c4c4c"
-                android:textColorHint="#9c9c9c"
+                android:hint="@string/all_apps_search_bar_hint"
+                android:textColorHint="@drawable/all_apps_search_hint"
                 android:textSize="16sp" />
-        </LinearLayout>
+        </FrameLayout>
 
     </com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
+    <View
+        android:id="@+id/nav_bar_bg"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_gravity="bottom"
+        android:background="@color/all_apps_navbar_color"
+        android:focusable="false"
+        android:visibility="invisible" />
 </com.android.launcher3.allapps.AllAppsContainerView>
\ No newline at end of file
diff --git a/res/layout/all_apps_container.xml b/res/layout/all_apps_container.xml
deleted file mode 100644
index 626edaf..0000000
--- a/res/layout/all_apps_container.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 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.
--->
-<!-- Both android:focusable and android:focusableInTouchMode are needed for
-     the view to get focus change events. -->
-<com.android.launcher3.allapps.AllAppsRecyclerViewContainerView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:elevation="15dp"
-    android:focusable="true"
-    android:focusableInTouchMode="true">
-
-    <!-- DO NOT CHANGE THE ID -->
-    <com.android.launcher3.allapps.AllAppsRecyclerView
-        android:id="@+id/apps_list_view"
-        android:theme="@style/Theme.Light.CustomOverscroll"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_gravity="center_horizontal|top"
-        android:clipToPadding="false"
-        android:focusable="true"
-        android:descendantFocusability="afterDescendants" />
-
-</com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
\ No newline at end of file
diff --git a/res/layout/all_apps_search_market_divider.xml b/res/layout/all_apps_divider.xml
similarity index 60%
copy from res/layout/all_apps_search_market_divider.xml
copy to res/layout/all_apps_divider.xml
index 3909781..1eaf685 100644
--- a/res/layout/all_apps_search_market_divider.xml
+++ b/res/layout/all_apps_divider.xml
@@ -13,15 +13,14 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<ImageView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:importantForAccessibility="no"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:gravity="center"
-    android:paddingTop="16dp"
-    android:paddingBottom="8dp"
-    android:paddingLeft="16dp"
-    android:paddingRight="16dp"
-    android:focusable="false"
-    android:scaleType="matrix"
-    android:src="@drawable/horizontal_line" />
\ No newline at end of file
+    android:paddingTop="@dimen/all_apps_divider_margin_vertical"
+    android:paddingBottom="@dimen/all_apps_divider_margin_vertical"
+    android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
+    android:paddingRight="@dimen/container_fastscroll_thumb_max_width"
+    android:src="@drawable/all_apps_divider"
+    android:scaleType="fitXY"
+    android:focusable="false" />
\ No newline at end of file
diff --git a/res/layout/all_apps_icon.xml b/res/layout/all_apps_icon.xml
index bb95c5f..3d4bef7 100644
--- a/res/layout/all_apps_icon.xml
+++ b/res/layout/all_apps_icon.xml
@@ -21,9 +21,7 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_gravity="center"
-    android:paddingTop="@dimen/all_apps_icon_top_bottom_padding"
-    android:paddingBottom="@dimen/all_apps_icon_top_bottom_padding"
     android:focusable="true"
-    android:background="@drawable/focusable_view_bg"
-    launcher:iconDisplay="all_apps" />
+    launcher:iconDisplay="all_apps"
+    launcher:centerVertically="true" />
 
diff --git a/res/layout/all_apps_prediction_bar_icon.xml b/res/layout/all_apps_prediction_bar_icon.xml
deleted file mode 100644
index f15aeaf..0000000
--- a/res/layout/all_apps_prediction_bar_icon.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.launcher3.BubbleTextView
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto"
-    style="@style/Icon.AllApps"
-    android:id="@+id/icon"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center"
-    android:paddingTop="@dimen/all_apps_prediction_icon_top_padding"
-    android:paddingBottom="@dimen/all_apps_prediction_icon_bottom_padding"
-    android:focusable="true"
-    android:background="@drawable/focusable_view_bg"
-    launcher:iconDisplay="all_apps" />
-
diff --git a/res/layout/all_apps_search_bar.xml b/res/layout/all_apps_search_bar.xml
deleted file mode 100644
index 69a66c8..0000000
--- a/res/layout/all_apps_search_bar.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2015 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.
--->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:background="@drawable/all_apps_search_bg" >
-
-    <LinearLayout
-        android:id="@+id/search_container"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/all_apps_search_bar_height"
-        android:layout_gravity="start|center_vertical"
-        android:orientation="horizontal"
-        android:visibility="invisible" >
-
-        <ImageView
-            android:id="@+id/dismiss_search_button"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical"
-            android:layout_marginLeft="16dp"
-            android:layout_marginStart="16dp"
-            android:contentDescription="@string/all_apps_button_label"
-            android:src="@drawable/ic_arrow_back_grey" />
-
-        <com.android.launcher3.ExtendedEditText
-            android:id="@+id/search_box_input"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:background="@android:color/transparent"
-            android:focusableInTouchMode="true"
-            android:gravity="fill_horizontal|center_vertical"
-            android:hint="@string/all_apps_search_bar_hint"
-            android:inputType="text|textNoSuggestions|textCapWords"
-            android:imeOptions="actionSearch|flagNoExtractUi"
-            android:maxLines="1"
-            android:paddingLeft="8dp"
-            android:scrollHorizontally="true"
-            android:singleLine="true"
-            android:textColor="#4c4c4c"
-            android:textColorHint="#9c9c9c"
-            android:textSize="16sp" />
-    </LinearLayout>
-
-    <ImageView
-        android:id="@+id/search_button"
-        android:layout_width="wrap_content"
-        android:layout_height="@dimen/all_apps_search_bar_height"
-        android:layout_gravity="end|center_vertical"
-        android:layout_marginEnd="16dp"
-        android:layout_marginRight="16dp"
-        android:contentDescription="@string/all_apps_search_bar_hint"
-        android:src="@drawable/ic_search_grey" />
-</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/all_apps_search_market_divider.xml b/res/layout/all_apps_search_divider.xml
similarity index 63%
rename from res/layout/all_apps_search_market_divider.xml
rename to res/layout/all_apps_search_divider.xml
index 3909781..d2ef691 100644
--- a/res/layout/all_apps_search_market_divider.xml
+++ b/res/layout/all_apps_search_divider.xml
@@ -13,15 +13,13 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<ImageView
-    xmlns:android="http://schemas.android.com/apk/res/android"
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:importantForAccessibility="no"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:gravity="center"
-    android:paddingTop="16dp"
-    android:paddingBottom="8dp"
-    android:paddingLeft="16dp"
-    android:paddingRight="16dp"
-    android:focusable="false"
-    android:scaleType="matrix"
-    android:src="@drawable/horizontal_line" />
\ No newline at end of file
+    android:paddingBottom="@dimen/all_apps_divider_margin_vertical"
+    android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
+    android:paddingRight="@dimen/container_fastscroll_thumb_max_width"
+    android:src="@drawable/all_apps_search_divider"
+    android:scaleType="fitXY"
+    android:focusable="false" />
\ No newline at end of file
diff --git a/res/layout/all_apps_search_market.xml b/res/layout/all_apps_search_market.xml
index 2e38ea0..3f19b25 100644
--- a/res/layout/all_apps_search_market.xml
+++ b/res/layout/all_apps_search_market.xml
@@ -18,12 +18,13 @@
     android:id="@+id/search_market_text"
     android:layout_width="match_parent"
     android:layout_height="48dp"
-    android:gravity="start|center_vertical"
-    android:paddingLeft="16dp"
-    android:paddingRight="16dp"
+    android:gravity="center"
+    android:paddingLeft="@dimen/container_fastscroll_thumb_max_width"
+    android:paddingRight="@dimen/container_fastscroll_thumb_max_width"
     android:fontFamily="sans-serif-medium"
     android:textSize="14sp"
-    android:textColor="@color/launcher_accent_color"
+    android:textColor="?android:attr/colorAccent"
+    android:text="@string/all_apps_search_market_message"
     android:textAllCaps="true"
     android:focusable="true"
-    android:background="@drawable/all_apps_search_market_bg" />
+    android:background="?android:selectableItemBackground" />
diff --git a/res/layout/deep_shortcut.xml b/res/layout/deep_shortcut.xml
new file mode 100644
index 0000000..7b42ec7
--- /dev/null
+++ b/res/layout/deep_shortcut.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<com.android.launcher3.shortcuts.DeepShortcutView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/bg_pill_width"
+    android:layout_height="@dimen/bg_pill_height"
+    android:elevation="@dimen/deep_shortcuts_elevation"
+    android:background="@drawable/bg_white_pill">
+
+<com.android.launcher3.shortcuts.DeepShortcutTextView
+        android:id="@+id/deep_shortcut"
+        style="@style/Icon.DeepShortcut"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:focusable="true"
+        android:background="@drawable/bg_pill_focused" />
+
+    <View
+        android:id="@+id/deep_shortcut_icon"
+        android:layout_width="@dimen/deep_shortcut_icon_size"
+        android:layout_height="@dimen/deep_shortcut_icon_size"
+        android:layout_margin="@dimen/deep_shortcut_padding_start"
+        android:layout_gravity="start" />
+
+</com.android.launcher3.shortcuts.DeepShortcutView>
diff --git a/res/layout/all_apps_search_market_divider.xml b/res/layout/deep_shortcuts_container.xml
similarity index 62%
copy from res/layout/all_apps_search_market_divider.xml
copy to res/layout/deep_shortcuts_container.xml
index 3909781..68bb60f 100644
--- a/res/layout/all_apps_search_market_divider.xml
+++ b/res/layout/deep_shortcuts_container.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- Copyright (C) 2016 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,15 +13,17 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<ImageView
+
+<com.android.launcher3.shortcuts.DeepShortcutsContainer
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
+    android:id="@+id/deep_shortcuts_container"
+    android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:gravity="center"
-    android:paddingTop="16dp"
+    android:paddingTop="8dp"
     android:paddingBottom="8dp"
-    android:paddingLeft="16dp"
-    android:paddingRight="16dp"
-    android:focusable="false"
-    android:scaleType="matrix"
-    android:src="@drawable/horizontal_line" />
\ No newline at end of file
+    android:clipToPadding="false"
+    android:clipChildren="false"
+    android:elevation="@dimen/deep_shortcuts_elevation"
+    android:orientation="vertical">
+
+</com.android.launcher3.shortcuts.DeepShortcutsContainer>
\ No newline at end of file
diff --git a/res/layout/drop_target_bar_horz.xml b/res/layout/drop_target_bar_horz.xml
new file mode 100644
index 0000000..ee22d1e
--- /dev/null
+++ b/res/layout/drop_target_bar_horz.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.launcher3.DropTargetBar
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/dynamic_grid_drop_target_size"
+    android:layout_gravity="center_horizontal|top"
+    android:focusable="false">
+
+    <FrameLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1" >
+
+        <!-- Delete target -->
+
+        <com.android.launcher3.DeleteDropTarget
+            launcher:hideParentOnDisable="true"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:id="@+id/delete_target_text"
+            style="@style/DropTargetButton"
+            android:text="@string/remove_drop_target_label" />
+    </FrameLayout>
+
+    <FrameLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1" >
+
+        <!-- App Info -->
+
+        <com.android.launcher3.InfoDropTarget
+            launcher:hideParentOnDisable="true"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:id="@+id/info_target_text"
+            style="@style/DropTargetButton"
+            android:text="@string/app_info_drop_target_label" />
+    </FrameLayout>
+
+    <FrameLayout
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1" >
+
+        <!-- Uninstall target -->
+
+        <com.android.launcher3.UninstallDropTarget
+            launcher:hideParentOnDisable="true"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:id="@+id/uninstall_target_text"
+            style="@style/DropTargetButton"
+            android:text="@string/uninstall_drop_target_label" />
+    </FrameLayout>
+
+</com.android.launcher3.DropTargetBar>
\ No newline at end of file
diff --git a/res/layout/drop_target_bar_vert.xml b/res/layout/drop_target_bar_vert.xml
new file mode 100644
index 0000000..10b1d7c
--- /dev/null
+++ b/res/layout/drop_target_bar_vert.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.launcher3.DropTargetBar
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/dynamic_grid_drop_target_size"
+    android:orientation="vertical"
+    android:layout_height="match_parent"
+    android:layout_gravity="left"
+    android:focusable="false"
+    android:paddingTop="@dimen/vert_drop_target_vertical_gap" >
+
+    <!-- Delete target -->
+    <com.android.launcher3.DeleteDropTarget
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/dynamic_grid_drop_target_size"
+        android:gravity="center"
+        android:paddingLeft="@dimen/vert_drop_target_horizontal_gap"
+        android:paddingRight="@dimen/vert_drop_target_horizontal_gap"
+        android:id="@+id/delete_target_text"
+        android:textColor="@android:color/white" />
+
+    <!-- Uninstall target -->
+    <com.android.launcher3.UninstallDropTarget
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/dynamic_grid_drop_target_size"
+        android:gravity="center"
+        android:paddingLeft="@dimen/vert_drop_target_horizontal_gap"
+        android:paddingRight="@dimen/vert_drop_target_horizontal_gap"
+        android:id="@+id/uninstall_target_text"
+        android:textColor="@android:color/white"
+        android:layout_marginTop="@dimen/vert_drop_target_vertical_gap"/>
+
+    <Space
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+
+    <!-- App Info -->
+    <com.android.launcher3.InfoDropTarget
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/dynamic_grid_drop_target_size"
+        android:gravity="center"
+        android:paddingLeft="@dimen/vert_drop_target_horizontal_gap"
+        android:paddingRight="@dimen/vert_drop_target_horizontal_gap"
+        android:id="@+id/info_target_text"
+        android:textColor="@android:color/white"
+        android:layout_marginBottom="64dp"/>
+
+</com.android.launcher3.DropTargetBar>
\ No newline at end of file
diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml
index 237af68..9eb8c9a 100644
--- a/res/layout/folder_icon.xml
+++ b/res/layout/folder_icon.xml
@@ -14,23 +14,16 @@
      limitations under the License.
 -->
 
-<com.android.launcher3.FolderIcon
+<com.android.launcher3.folder.FolderIcon
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
     android:focusable="true" >
-    <ImageView
-        android:id="@+id/preview_background"
-        android:layout_gravity="center_horizontal"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:antialias="true"
-        android:src="@drawable/portal_ring_inner"/>
     <com.android.launcher3.BubbleTextView
         style="@style/Icon"
         android:id="@+id/folder_icon_name"
         android:layout_gravity="top"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
-</com.android.launcher3.FolderIcon>
+</com.android.launcher3.folder.FolderIcon>
diff --git a/res/layout/longpress_cling_content.xml b/res/layout/longpress_cling_content.xml
deleted file mode 100644
index 47a8e97..0000000
--- a/res/layout/longpress_cling_content.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:paddingBottom="24dp"
-    android:paddingTop="36dp" >
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:paddingLeft="36dp"
-        android:paddingRight="36dp"
-        android:text="@string/workspace_cling_longpress_title"
-        android:textColor="#E1000000"
-        android:textSize="24sp" />
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="16dp"
-        android:paddingLeft="36dp"
-        android:paddingRight="36dp"
-        android:text="@string/workspace_cling_longpress_description"
-        android:textColor="#99000000"
-        android:textSize="16sp" />
-
-    <Button
-        android:id="@+id/cling_dismiss_longpress_info"
-        style="?android:attr/buttonBarButtonStyle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="end"
-        android:layout_marginRight="12dp"
-        android:layout_marginTop="27dp"
-        android:fontFamily="sans-serif-medium"
-        android:paddingLeft="24dp"
-        android:paddingRight="24dp"
-        android:text="@string/workspace_cling_longpress_dismiss"
-        android:textColor="#FFFFFFFF"
-        android:textSize="14sp" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/longpress_cling_welcome_content.xml b/res/layout/longpress_cling_welcome_content.xml
deleted file mode 100644
index dd4f8d7..0000000
--- a/res/layout/longpress_cling_welcome_content.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:paddingBottom="24dp"
-    android:paddingTop="36dp" >
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="8dp"
-        android:paddingLeft="36dp"
-        android:paddingRight="36dp"
-        android:text="@string/first_run_cling_title"
-        android:textColor="#E1000000"
-        android:textSize="34sp" />
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="5.3dp"
-        android:fontFamily="sans-serif-medium"
-        android:paddingLeft="36dp"
-        android:paddingRight="36dp"
-        android:text="@string/workspace_cling_longpress_title"
-        android:textColor="#E1000000"
-        android:textSize="20sp" />
-
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:paddingLeft="36dp"
-        android:paddingRight="36dp"
-        android:text="@string/workspace_cling_longpress_description"
-        android:textColor="#99000000"
-        android:textSize="16sp" />
-
-    <Button
-        android:id="@+id/cling_dismiss_longpress_info"
-        style="?android:attr/buttonBarButtonStyle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="end"
-        android:layout_marginRight="12dp"
-        android:layout_marginTop="27dp"
-        android:fontFamily="sans-serif-medium"
-        android:paddingLeft="24dp"
-        android:paddingRight="24dp"
-        android:text="@string/workspace_cling_longpress_dismiss"
-        android:textColor="#FFFFFFFF"
-        android:textSize="14sp" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/overview_panel.xml b/res/layout/overview_panel.xml
index 4f54f1d..9ba3f09 100644
--- a/res/layout/overview_panel.xml
+++ b/res/layout/overview_panel.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2013 The Android Open Source Project
+     Copyright (C) 2016 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,11 +15,11 @@
      limitations under the License.
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center_horizontal|bottom"
-    android:gravity="top"
-    android:orientation="horizontal" >
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_gravity="center_horizontal|bottom"
+      android:gravity="top"
+      android:orientation="horizontal" >
 
     <TextView
         android:id="@+id/wallpaper_button"
@@ -27,14 +27,14 @@
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:drawablePadding="4dp"
-        android:drawableTop="@drawable/wallpaper_button"
+        android:drawableTop="@drawable/ic_wallpaper"
         android:fontFamily="sans-serif-condensed"
         android:gravity="center_horizontal"
+        android:stateListAnimator="@animator/overview_button_anim"
         android:text="@string/wallpaper_button_text"
         android:textAllCaps="true"
         android:textColor="@android:color/white"
-        android:textSize="12sp"
-        android:focusable="true" />
+        android:textSize="12sp" />
 
     <TextView
         android:id="@+id/widget_button"
@@ -42,14 +42,14 @@
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:drawablePadding="4dp"
-        android:drawableTop="@drawable/widget_button"
+        android:drawableTop="@drawable/ic_widget"
         android:fontFamily="sans-serif-condensed"
         android:gravity="center_horizontal"
+        android:stateListAnimator="@animator/overview_button_anim"
         android:text="@string/widget_button_text"
         android:textAllCaps="true"
         android:textColor="@android:color/white"
-        android:textSize="12sp"
-        android:focusable="true" />
+        android:textSize="12sp" />
 
     <TextView
         android:id="@+id/settings_button"
@@ -57,13 +57,13 @@
         android:layout_height="wrap_content"
         android:layout_weight="1"
         android:drawablePadding="4dp"
-        android:drawableTop="@drawable/setting_button"
+        android:drawableTop="@drawable/ic_setting"
         android:fontFamily="sans-serif-condensed"
         android:gravity="center_horizontal"
+        android:stateListAnimator="@animator/overview_button_anim"
         android:text="@string/settings_button_text"
         android:textAllCaps="true"
         android:textColor="@android:color/white"
-        android:textSize="12sp"
-        android:focusable="true" />
+        android:textSize="12sp" />
 
 </LinearLayout>
\ No newline at end of file
diff --git a/res/layout/page_indicator.xml b/res/layout/page_indicator.xml
index 68fe3dd..2e1b57f 100644
--- a/res/layout/page_indicator.xml
+++ b/res/layout/page_indicator.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
+<!-- Copyright (C) 2016 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,9 +13,15 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.PageIndicator
+
+<com.android.launcher3.pageindicators.PageIndicatorLineCaret
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto"
-    android:animateLayoutChanges="true"
-    launcher:windowSize="@integer/config_maxNumberOfPageIndicatorsToShow">
-</com.android.launcher3.PageIndicator>
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/dynamic_grid_page_indicator_height">
+        <ImageView
+            android:id="@+id/all_apps_handle"
+            android:layout_width="48dp"
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            android:scaleType="centerInside"/>
+</com.android.launcher3.pageindicators.PageIndicatorLineCaret>
diff --git a/res/layout/page_indicator_marker.xml b/res/layout/page_indicator_marker.xml
deleted file mode 100644
index 564a958..0000000
--- a/res/layout/page_indicator_marker.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.launcher3.PageIndicatorMarker
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto"
-    android:layout_width="12dp"
-    android:layout_height="12dp"
-    android:layout_gravity="center_vertical">
-    <ImageView
-        android:id="@+id/inactive"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:scaleType="centerInside"
-        android:src="@drawable/ic_pageindicator_default"
-        />
-    <ImageView
-        android:id="@+id/active"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:scaleType="centerInside"
-        android:src="@drawable/ic_pageindicator_current"
-        android:alpha="0"
-        android:scaleX="0.5"
-        android:scaleY="0.5"
-        />
-</com.android.launcher3.PageIndicatorMarker>
diff --git a/res/drawable/horizontal_line.xml b/res/layout/qsb_blocker_view.xml
similarity index 71%
copy from res/drawable/horizontal_line.xml
copy to res/layout/qsb_blocker_view.xml
index 3f3f17e3..58a148e 100644
--- a/res/drawable/horizontal_line.xml
+++ b/res/layout/qsb_blocker_view.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2015 The Android Open Source Project
+     Copyright (C) 2016 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.
@@ -14,8 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    android:shape="rectangle">
-    <size android:height="1dp" />
-    <solid android:color="#ddd" />
-</shape>
+<com.android.launcher3.QsbBlockerView
+    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
diff --git a/res/layout/qsb_container.xml b/res/layout/qsb_container.xml
new file mode 100644
index 0000000..b75e3b5
--- /dev/null
+++ b/res/layout/qsb_container.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<com.android.launcher3.QsbContainerView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:id="@+id/qsb_container"
+        android:padding="0dp" >
+
+    <fragment
+        android:name="com.android.launcher3.QsbContainerView$QsbFragment"
+        android:layout_width="match_parent"
+        android:tag="qsb_view"
+        android:layout_height="match_parent"/>
+</com.android.launcher3.QsbContainerView>
\ No newline at end of file
diff --git a/res/layout/qsb_default_view.xml b/res/layout/qsb_default_view.xml
new file mode 100644
index 0000000..82bdea5
--- /dev/null
+++ b/res/layout/qsb_default_view.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:layout_margin="16dp"
+        android:layout_gravity="center_vertical"
+        android:background="@drawable/quantum_panel_shape"
+        android:elevation="2dp"
+        android:orientation="horizontal">
+
+        <TextView
+            android:layout_width="0dp"
+            android:id="@+id/btn_qsb_search"
+            android:layout_height="match_parent"
+            android:layout_weight="1"
+            android:gravity="center_vertical"
+            android:paddingStart="16dp"
+            android:text="@string/abandoned_search"
+            android:textColor="@color/quantum_panel_text_color"
+            android:textAppearance="?android:textAppearanceMedium"
+            android:clickable="true"
+            android:background="?android:attr/selectableItemBackground" />
+        <ImageView
+            android:layout_width="48dp"
+            android:id="@+id/btn_qsb_setup"
+            android:clickable="true"
+            android:visibility="gone"
+            android:layout_height="match_parent"
+            android:src="@drawable/ic_setting"
+            android:tint="@color/quantum_panel_text_color"
+            android:contentDescription="@string/gadget_setup_text"
+            android:padding="8dp"
+            android:background="?android:attr/selectableItemBackground" />
+    </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/search_drop_target_bar.xml b/res/layout/search_drop_target_bar.xml
deleted file mode 100644
index 4737ee1..0000000
--- a/res/layout/search_drop_target_bar.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.launcher3.SearchDropTargetBar xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:focusable="false" >
-
-    <!-- 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" >
-
-        <FrameLayout
-            style="@style/DropTargetButtonContainer"
-            android:layout_weight="1" >
-
-            <!-- Delete target -->
-
-            <com.android.launcher3.DeleteDropTarget
-                android:id="@+id/delete_target_text"
-                style="@style/DropTargetButton"
-                android:text="@string/delete_target_label" />
-        </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:text="@string/info_target_label" />
-        </FrameLayout>
-
-        <FrameLayout
-            style="@style/DropTargetButtonContainer"
-            android:layout_weight="1" >
-
-            <!-- Uninstall target -->
-
-            <com.android.launcher3.UninstallDropTarget
-                android:id="@+id/uninstall_target_text"
-                style="@style/DropTargetButton"
-                android:text="@string/delete_target_uninstall_label" />
-        </FrameLayout>
-    </LinearLayout>
-
-</com.android.launcher3.SearchDropTargetBar>
\ No newline at end of file
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index 252ebf0..da2a861 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -14,7 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.Folder xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.folder.Folder xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
@@ -22,27 +22,14 @@
     android:elevation="5dp"
     android:orientation="vertical" >
 
-    <FrameLayout
-        android:id="@+id/folder_content_wrapper"
+    <com.android.launcher3.folder.FolderPagedView
+        android:id="@+id/folder_content"
         android:layout_width="match_parent"
-        android:layout_height="match_parent" >
-
-        <!-- Actual size of the indicator doesn't matter as it is scaled to match the view size -->
-
-        <com.android.launcher3.FocusIndicatorView
-            android:id="@+id/focus_indicator"
-            android:layout_width="20dp"
-            android:layout_height="20dp" />
-
-        <com.android.launcher3.FolderPagedView
-            android:id="@+id/folder_content"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:paddingLeft="4dp"
-            android:paddingRight="4dp"
-            android:paddingTop="8dp"
-            launcher:pageIndicator="@+id/folder_page_indicator" />
-    </FrameLayout>
+        android:layout_height="match_parent"
+        android:paddingLeft="8dp"
+        android:paddingRight="8dp"
+        android:paddingTop="6dp"
+        launcher:pageIndicator="@+id/folder_page_indicator" />
 
     <LinearLayout
         android:id="@+id/folder_footer"
@@ -50,8 +37,8 @@
         android:layout_height="wrap_content"
         android:clipChildren="false"
         android:orientation="horizontal"
-        android:paddingLeft="8dp"
-        android:paddingRight="8dp" >
+        android:paddingLeft="12dp"
+        android:paddingRight="12dp" >
 
         <com.android.launcher3.ExtendedEditText
             android:id="@+id/folder_name"
@@ -64,21 +51,23 @@
             android:gravity="center_horizontal"
             android:hint="@string/folder_hint_text"
             android:imeOptions="flagNoExtractUi"
-            android:paddingBottom="8dp"
-            android:paddingTop="4dp"
+            android:paddingBottom="@dimen/folder_label_padding_bottom"
+            android:paddingTop="@dimen/folder_label_padding_top"
             android:singleLine="true"
             android:textColor="#ff777777"
+            android:includeFontPadding="false"
             android:textColorHighlight="#ffCCCCCC"
             android:textColorHint="#ff808080"
-            android:textSize="14sp" />
+            android:textSize="@dimen/folder_label_text_size" />
 
-        <include
+        <com.android.launcher3.pageindicators.PageIndicatorDots
             android:id="@+id/folder_page_indicator"
-            android:layout_width="wrap_content"
-            android:layout_height="12dp"
             android:layout_gravity="center_vertical"
-            layout="@layout/page_indicator" />
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:elevation="1dp"
+            />
 
     </LinearLayout>
 
-</com.android.launcher3.Folder>
\ No newline at end of file
+</com.android.launcher3.folder.Folder>
\ No newline at end of file
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
index 75b5c48..a8af201 100644
--- a/res/layout/user_folder_icon_normalized.xml
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -14,7 +14,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.launcher3.Folder xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.launcher3.folder.Folder xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
@@ -22,27 +22,14 @@
     android:elevation="5dp"
     android:orientation="vertical" >
 
-    <FrameLayout
-        android:id="@+id/folder_content_wrapper"
+    <com.android.launcher3.folder.FolderPagedView
+        android:id="@+id/folder_content"
         android:layout_width="match_parent"
-        android:layout_height="match_parent" >
-
-        <!-- Actual size of the indicator doesn't matter as it is scaled to match the view size -->
-
-        <com.android.launcher3.FocusIndicatorView
-            android:id="@+id/focus_indicator"
-            android:layout_width="20dp"
-            android:layout_height="20dp" />
-
-        <com.android.launcher3.FolderPagedView
-            android:id="@+id/folder_content"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:paddingLeft="8dp"
-            android:paddingRight="8dp"
-            android:paddingTop="8dp"
-            launcher:pageIndicator="@+id/folder_page_indicator" />
-    </FrameLayout>
+        android:layout_height="match_parent"
+        android:paddingLeft="8dp"
+        android:paddingRight="8dp"
+        android:paddingTop="6dp"
+        launcher:pageIndicator="@+id/folder_page_indicator" />
 
     <LinearLayout
         android:id="@+id/folder_footer"
@@ -50,8 +37,8 @@
         android:layout_height="wrap_content"
         android:clipChildren="false"
         android:orientation="horizontal"
-        android:paddingLeft="8dp"
-        android:paddingRight="8dp" >
+        android:paddingLeft="12dp"
+        android:paddingRight="12dp" >
 
         <com.android.launcher3.ExtendedEditText
             android:id="@+id/folder_name"
@@ -65,21 +52,22 @@
             android:gravity="center_horizontal"
             android:hint="@string/folder_hint_text"
             android:imeOptions="flagNoExtractUi"
-            android:paddingBottom="12dp"
-            android:paddingTop="4dp"
+            android:paddingBottom="@dimen/folder_label_padding_bottom"
+            android:paddingTop="@dimen/folder_label_padding_top"
             android:singleLine="true"
             android:textColor="#EE777777"
             android:textColorHighlight="#ffCCCCCC"
             android:textColorHint="#ff808080"
-            android:textSize="14sp" />
+            android:textSize="@dimen/folder_label_text_size" />
 
-        <include
+        <com.android.launcher3.pageindicators.PageIndicatorDots
             android:id="@+id/folder_page_indicator"
-            android:layout_width="wrap_content"
-            android:layout_height="12dp"
             android:layout_gravity="center_vertical"
-            layout="@layout/page_indicator" />
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:elevation="1dp"
+            />
 
     </LinearLayout>
 
-</com.android.launcher3.Folder>
+</com.android.launcher3.folder.Folder>
diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml
index 7fefeba..15f369f 100644
--- a/res/layout/widget_cell.xml
+++ b/res/layout/widget_cell.xml
@@ -21,7 +21,7 @@
     android:layout_weight="1"
     android:orientation="vertical"
     android:focusable="true"
-    android:background="@color/widgets_cell_color"
+    android:background="?android:attr/colorPrimary"
     android:gravity="center_horizontal">
 
     <LinearLayout
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index 1d593df..30a34d4 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -19,7 +19,7 @@
     android:id="@+id/widgets_cell_list_container"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@color/widgets_cell_color"
+    android:background="?android:attr/colorPrimary"
     android:orientation="vertical"
     android:focusable="true"
     android:descendantFocusability="afterDescendants">
@@ -30,7 +30,7 @@
         android:id="@+id/section"
         android:layout_width="match_parent"
         android:layout_height="@dimen/widget_section_height"
-        android:background="@color/quantum_panel_bg_color_dark"
+        android:background="?attr/colorSecondary"
         android:drawablePadding="@dimen/widget_section_horizontal_padding"
         android:ellipsize="end"
         android:focusable="true"
@@ -51,7 +51,7 @@
 
     <HorizontalScrollView
         android:id="@+id/widgets_scroll_container"
-        android:theme="@style/Theme.Dark.CustomOverscroll"
+        android:theme="@style/CustomOverscroll.Dark"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:scrollbars="none">
@@ -59,8 +59,8 @@
             android:id="@+id/widgets_cell_list"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginStart="@dimen/widget_row_padding"
-            android:layout_marginLeft="@dimen/widget_row_padding"
+            android:paddingStart="@dimen/widget_row_padding"
+            android:paddingEnd="0dp"
             android:orientation="horizontal"
             android:divider="@drawable/widgets_row_divider"
             android:showDividers="middle"/>
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index c51ec80..c4431be 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -23,7 +23,8 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:descendantFocusability="afterDescendants"
-    launcher:revealBackground="@drawable/quantum_panel_shape_dark">
+    launcher:revealBackground="@drawable/quantum_panel_shape_dark"
+    android:theme="@style/WidgetContainerTheme">
 
     <View
         android:id="@+id/reveal_view"
@@ -44,9 +45,15 @@
 
         <com.android.launcher3.widget.WidgetsRecyclerView
             android:id="@+id/widgets_list_view"
-            android:theme="@style/Theme.Dark.CustomOverscroll"
+            android:theme="@style/CustomOverscroll.Dark"
             android:layout_width="match_parent"
             android:layout_height="match_parent" />
+
+        <ProgressBar
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/loader"
+            android:layout_gravity="center" />
     </FrameLayout>
 
 
diff --git a/res/layout/workspace_screen.xml b/res/layout/workspace_screen.xml
index 83b319b..faf6885 100644
--- a/res/layout/workspace_screen.xml
+++ b/res/layout/workspace_screen.xml
@@ -17,9 +17,6 @@
 <com.android.launcher3.CellLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
-
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:hapticFeedbackEnabled="false"
-
-    launcher:maxGap="@dimen/workspace_max_gap" />
+    android:hapticFeedbackEnabled="false" />
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index f9e1cff..9152ea5 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -26,20 +26,22 @@
     <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>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Legstukke gedeaktiveer in Veiligmodus"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Kortpad is nie beskikbaar nie"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Raak en hou om \'n legstuk op te tel."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dubbeltik en hou om \'n legstuk op te tel of gebruik gepasmaakte handelinge."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Deursoek programme …"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breed by %2$d hoog"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Deursoek programme"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Laai tans programme …"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Geen programme gevind wat met \"<xliff:g id="QUERY">%1$s</xliff:g>\" ooreenstem nie"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Gaan na <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Soek meer programme"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Niks meer spasie op die tuisskerm nie."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Geen plek meer in die Gunstelinge-laai nie"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Programme"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Programmelys"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Tuis"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Verwyder"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Deïnstalleer"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Programinligting"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Verwyder"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleer"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Programinligting"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installeer kortpaaie"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Laat \'n program toe om kortpaaie by te voeg sonder gebruikerinmenging."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"lees Tuis-instellings en -kortpaaie"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Stel op"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dit is \'n stelselprogram en kan nie gedeïnstalleer word nie."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Naamlose vouer"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Het <xliff:g id="APP_NAME">%1$s</xliff:g> gedeaktiveer"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Bladsy %1$d van %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Tuisskerm %1$d van %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nuwe tuisskermbladsy"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Welkom"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopieer jou program-ikone"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Voer ikone en vouers vanaf jou ou tuisskerms in?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPIEER IKONE"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"BEGIN VAN NUUTS AF"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Muurpapiere, legstukke en instellings"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Raak en hou agtergrond om te pasmaak"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"HET DIT"</string>
     <string name="folder_opened" msgid="94695026776264709">"Vouer oopgemaak, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Raak om vouer toe te maak"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Raak om hernoem te stoor"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Tik om die vouer toe te maak"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tik om nuwe naam te stoor"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Vouer is gesluit"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Vouer hernoem na <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Muurpapiere"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Instellings"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gedeaktiveer deur jou administrateur"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Laat draai toe"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Oorsig"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Laat toe dat tuisskerm gedraai word"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Wanneer foon gedraai word"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Huidige vertooninstelling laat nie rotasie toe nie"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Verwyder"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Soek"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Verminder breedte"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Verminder hoogte"</string>
     <string name="widget_resized" msgid="9130327887929620">"Legstukgrootte is verander na breedte <xliff:g id="NUMBER_0">%1$s</xliff:g> hoogte <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Kortpaaie"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> kortpaaie vir <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 8bf17f7..c20dc12 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"አቋራጭ አይገኝም"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"ፍርግም ለማንሳት ይንኩ እና ይያዙት"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"አንድ ንዑስ ፕሮግራም ለመምረጥ ወይም ብጁ እርምጃዎችን ለመጠቀም ሁለቴ መታ አድርገው ይያዙ።"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"መተግበሪያዎችን ይፈልጉ…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ስፋት በ%2$d ከፍታ"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"መተግበሪያዎችን ይፈልጉ"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"መተግበሪያዎችን በመጫን ላይ..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"ከ«<xliff:g id="QUERY">%1$s</xliff:g>» ጋር የሚዛመዱ ምንም መተግበሪያዎች አልተገኙም"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"ወደ <xliff:g id="QUERY">%1$s</xliff:g> ሂድ"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"ተጨማሪ መተግበሪያዎች ይፈልጉ"</string>
     <string name="out_of_space" msgid="4691004494942118364">"በዚህ መነሻ ማያ ገጽ ላይ ምንም ቦታ የለም።"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"በተወዳጆች መሣቢያ ውስጥ ተጨማሪ ቦታ የለም"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"መተግበሪያዎች"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"የመተግበሪያዎች ዝርዝር"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"መነሻ"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"አስወግድ"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"አራግፍ"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"የመተግበሪያ መረጃ"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"አስወግድ"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"አራግፍ"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"የመተግበሪያ መረጃ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"አቋራጮችን ይጭናል"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"መተግበሪያው ያለተጠቃሚ ጣልቃ ገብነት አቋራጭ እንዲያክል ያስችለዋል።"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"የመነሻ ቅንብሮች እና አቋራጮችን ያነባል"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"ማዋቀሪያ"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ይህ የስርዓት መተግበሪያ ነው እና ማራገፍ አይቻልም።"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"ስም-አልባ አቃፊ"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ተሰናክሏል"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"ገጽ %1$d ከ%2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"መነሻ ማያ ገጽ %1$d ከ%2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"አዲስ የመነሻ ማያ ገጽ"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"እንኳን በደህና መጡ"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"የመተግበሪያ አዶዎችዎን ይቅዱ"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"አዶዎች እና አቃፊዎች ከድሮው የመነሻ ማያ ገጾችዎ ይምጡ?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"አዶዎችን ይቅዱ"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"እንደ አዲስ ይጀምሩ"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"የግድግዳ ወረቀቶች፣ ንዑስ ፕሮግራሞች እና ቅንብሮች"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"ለማበጀት ጀርባውን ነክተው ይያዙት"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ገባኝ"</string>
     <string name="folder_opened" msgid="94695026776264709">"አቃፊ ተከፍቷል፣ <xliff:g id="WIDTH">%1$d</xliff:g> በ<xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"አቃፊን ለመዝጋት ይንኩ"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"ዳግም የተሰየመውን ለማስቀመጥ ይንኩ"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"አቃፊን ለመዝጋት መታ ያድርጉ"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"ዳግም የተሰጠውን ስም ለማስቀመጥ መታ ያድርጉ"</string>
     <string name="folder_closed" msgid="4100806530910930934">"አቃፊ ተዘግቷል"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"አቃፊ <xliff:g id="NAME">%1$s</xliff:g> ተብሎ ዳግም ተሰይሟል"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"አቃፊ፦ <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"የግድግዳ ወረቀቶች"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"ቅንብሮች"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"በእርስዎ አስተዳዳሪ የተሰናከለ"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"ማሽከርከርን ይፍቀዱ"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"አጠቃላይ ዕይታ"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"የመነሻ ማያ ገጽ ማሽከርከርን ይፍቀዱ"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"ስልኩ ሲዞር"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"የአሁኑ የማሳያ ቅንብር ማሽከርከርን አይፈቅድም"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"የማይታወቅ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"አስወግድ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ፈልግ"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"ስፋት ይቀንሱ"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"ቁመት ይቀንሱ"</string>
     <string name="widget_resized" msgid="9130327887929620">"የመግብር መጠን ወደ ስፋት <xliff:g id="NUMBER_0">%1$s</xliff:g> ቁመት <xliff:g id="NUMBER_1">%2$s</xliff:g> ተለውጧል"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"አቋራጮች"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> የ<xliff:g id="APP_NAME">%2$s</xliff:g> አቋራጮች"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index ba4def2..c80d162 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"الاختصار غير متاح"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"المس مع الاستمرار لاختيار إحدى الأدوات."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"انقر نقرًا مزدوجًا مع الاستمرار لاختيار أداة أو استخدم الإجراءات المخصصة."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"البحث في التطبيقات…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏العرض %1$d الطول %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"البحث في التطبيقات"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"جارٍ تحميل التطبيقات…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"لم يتم العثور على أية تطبيقات تتطابق مع \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"الانتقال إلى <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"البحث عن مزيد من التطبيقات"</string>
     <string name="out_of_space" msgid="4691004494942118364">"ليس هناك مساحة أخرى في هذه الشاشة الرئيسية."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"لا يوجد المزيد من الحقول في علبة المفضلة"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"التطبيقات"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"قائمة التطبيقات"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"الرئيسية"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"إزالة"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"إلغاء التثبيت"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"معلومات عن التطبيق"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"إزالة"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"إلغاء التثبيت"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"معلومات عن التطبيق"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"تثبيت اختصارات"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"للسماح لتطبيق ما بإضافة اختصارات بدون تدخل المستخدم."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"قراءة إعدادات واختصارات الشاشة الرئيسية"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"الإعداد"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"هذا تطبيق نظام وتتعذر إزالته."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"مجلد بدون اسم"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"تم تعطيل <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"‏الصفحة %1$d من %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"‏الشاشة الرئيسية %1$d من %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"صفحة الشاشة الرئيسية الجديدة"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"مرحبًا"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"نسخ رموز التطبيقات"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"هل تريد استيراد رموز ومجلدات من الشاشات الرئيسية القديمة؟"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"نسخ الرموز"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"بداية جديدة"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"الخلفيات والأدوات والإعدادات"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"المس مع الاستمرار الخلفية لتخصيصها"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"حسنًا"</string>
     <string name="folder_opened" msgid="94695026776264709">"تم فتح المجلد، بمقاس <xliff:g id="WIDTH">%1$d</xliff:g> في <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"المس لإغلاق المجلد"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"المس لحفظ إعادة التسمية"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"انقر لإغلاق المجلد"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"انقر لحفظ الاسم الجديد"</string>
     <string name="folder_closed" msgid="4100806530910930934">"تم إغلاق المجلد"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"تمت إعادة تسمية المجلد إلى <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"الخلفيات"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"الإعدادات"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"عطَّل المشرف هذه الميزة"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"السماح بالتدوير"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"نظرة عامة"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"السماح بتدوير الشاشة الرئيسية"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"عند تدوير الهاتف"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"لا يسمح إعداد العرض الحالي بالتدوير"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"غير معروفة"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"إزالة"</string>
     <string name="abandoned_search" msgid="891119232568284442">"بحث"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"تقليل العرض"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"تقليل الارتفاع"</string>
     <string name="widget_resized" msgid="9130327887929620">"تم تغيير حجم الأداة إلى العرض <xliff:g id="NUMBER_0">%1$s</xliff:g> والارتفاع <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"الاختصارات"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> اختصار لتطبيق <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index 476f2ab..1a4f31b 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Tətbiq əlçatmazdır"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Güvənli rejimdə icazə verilməyən tətbiq endirildi"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Vidcetlər Güvənli rejimdə deaktiv edilib"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Qısayol əlçatan deyil"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Vidceti götürmək üçün toxunub saxlayın."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Vidceti götürmək üçün &amp; iki dəfə toxunub saxlayın və ya fərdi fəaliyyətləri istifadə edin."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Tətbiqləri Axtarın..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%2$d hündürlük %1$d enində"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Tətbiq Axtarın"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Tətbiqlər endirilir..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" sorğusuna uyğun Tətbiqlər tapılmadı"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> daxil olun"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Daha çox tətbiq üçün axtarış edin"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Bu Əsas ekranda boş yer yoxdur."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritlər-də yer yoxdur"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Tətbiqlər"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Tətbiq siyahısı"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Əsas səhifə"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Kənarlaşdır"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Sistemdən sil"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Tətbiq məlumatı"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Silin"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Sistemdən sil"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Tətbiq məlumatı"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"qısayolları quraşdır"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Tətbiqə istifadəçi müdaxiləsi olmadan qısayolları əlavə etməyə icazə verir."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"Əsas Səhifə ayarlarını və qısayolları oxuyun"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Quraşdırma"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu sistem tətbiqi olduğu üçün sistemdən silinə bilməz."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Adsız Qovluq"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> deaktiv edildi"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Səhifə %1$d of %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Əsas Səhifə ekranı %1$d of %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Yeni əsas ekran səhifəsi"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Xoş gəlmisiniz"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Tətbiq ikonalarınızı kopyalayın"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Köhnə Əsas ekranınızda olan ikonalar və qovluqlar import edilsin?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"İKONALARI KOPYALA"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"YENİDƏN BAŞLA"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Divar kağızları, vidcetlər və ayarlar"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Fərdiləşdirmək üçün arxa fona toxunaraq saxlayın"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ANLADIM"</string>
     <string name="folder_opened" msgid="94695026776264709">"Qovluq açıldı, <xliff:g id="HEIGHT">%2$d</xliff:g> hündürlük ilə <xliff:g id="WIDTH">%1$d</xliff:g> enində"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Qovluğu bağlamaq üçün toxunun"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Ad dəyişikliyini yadda saxlamaq üçün toxunun"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Qovluq bağlamaq üçün toxunun"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Ad dəyişikliyini yadda saxlamaq üçün toxunun"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Qovluq bağlıdır"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Qovluq adı <xliff:g id="NAME">%1$s</xliff:g> ilə dəyişdirildi"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Divar kağızları"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Ayarlar"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Admininiz tərəfindən deaktiv edilib"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Fırlatmağa icazə verin"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"İcmal"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Əsas ekranın firlanmağına icazə verin"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon çevrilən zaman"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Cari Ekran ayarı fırlatmağa icazə vermir"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Naməlum"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Yığışdır"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Axtarış"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Eni azaldın"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Hündürlüyü azaldın"</string>
     <string name="widget_resized" msgid="9130327887929620">"Vidcetin eni <xliff:g id="NUMBER_0">%1$s</xliff:g> hündürlüyü <xliff:g id="NUMBER_1">%2$s</xliff:g> kimi ölçüləndirildi"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Qısa yollar"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> üçün <xliff:g id="APP_NAME">%2$s</xliff:g> qısa yolu"</string>
 </resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index d2168c2..a9344f9 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Aplikacija nije dostupna"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Preuzeta aplikacija je onemogućena u Bezbednom režimu"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Vidžeti su onemogućeni u Bezbednom režimu"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Prečica nije dostupna"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Dodirnite i zadržite da biste izabrali vidžet."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dvaput dodirnite i zadržite da biste izabrali vidžet ili koristite prilagođene radnje."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Pretražite aplikacije..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"širina od %1$d i visina od %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pretražite aplikacije"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikacije se učitavaju..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nije pronađena nijedna aplikacija za „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Idi na aplikaciju <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pretraži još aplikacija"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Nema više prostora na ovom početnom ekranu."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora na traci Omiljeno"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacije"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Lista aplikacija"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Početna"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Ukloni"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Deinstaliraj"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Informacije o aplikaciji"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Ukloni"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Inform. o aplikaciji"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečica"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Dozvoljava aplikaciji da dodaje prečice bez intervencije korisnika."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"čitanje podešavanja i prečica na početnom ekranu"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Podešavanje"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je sistemska aplikacija i ne može da se deinstalira."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Neimenovani direktorijum"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućena"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d. stranica od %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d. početni ekran od %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog ekrana"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Dobro došli"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopirajte ikone aplikacija"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Uvesti ikone i direktorijume sa starih početnih ekrana?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPIRAJTE IKONE"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"POČNITE ISPOČETKA"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Pozadine, vidžeti i podešavanja"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Dodirnite i zadržite pozadinu da biste prilagodili"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"VAŽI"</string>
     <string name="folder_opened" msgid="94695026776264709">"Direktorijum je otvoren, <xliff:g id="WIDTH">%1$d</xliff:g> puta <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Dodirnite da biste zatvorili direktorijum"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Dodirnite da biste sačuvali promenu imena"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Dodirnite da biste zatvorili direktorijum"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da biste sačuvali preimenovanje"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Direktorijum je zatvoren"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Direktorijum je preimenovan u <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Direktorijum: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Podešavanja"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator je onemogućio"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Dozvoli rotaciju"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Dozvoli rotaciju početnog ekrana"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon rotira"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Aktuelno podešavanje prikaza ne dozvoljava rotaciju"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Pretraži"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Smanji širinu"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Smanji visinu"</string>
     <string name="widget_resized" msgid="9130327887929620">"Veličina vidžeta je promenjena na širinu <xliff:g id="NUMBER_0">%1$s</xliff:g> i visinu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Prečice"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> prečice(a) za <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-be-rBY/strings.xml b/res/values-be-rBY/strings.xml
index 47fbf8c..f5dba2d 100644
--- a/res/values-be-rBY/strings.xml
+++ b/res/values-be-rBY/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"Ярлык недаступны"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Дакраніцеся і ўтрымлiвайце віджэт, каб выбр. яго."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Дакраніцеся двойчы і ўтрымлівайце, каб выбраць віджэт або выкарыстоўваць карыстальніцкія дзеянні."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Пошук у Праграмах..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Шырына: %1$d, вышыня: %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Пошук у Праграмах"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Ідзе загрузка праграм…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Праграм, якія адпавядаюць запыту \"<xliff:g id="QUERY">%1$s</xliff:g>\", не знойдзена"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Перайсці да <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Шукаць іншыя праграмы"</string>
     <string name="out_of_space" msgid="4691004494942118364">"На гэтым Галоўным экране больш няма месца."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"У латку \"Абранае\" больш няма месца"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Праграмы"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Спіс праграм"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Галоўная"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Выдаліць"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Выдаліць"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Звесткі пра праграму"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Выдаліць"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Выдаліць"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Звесткі пра праграму"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"усталёўваць ярлыкі"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Дазваляе праграмам дадаваць ярлыкі без умяшання карыстальніка."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"счытваць налады і ярлыкі на Галоўнай старонцы"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Наладжванне"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Гэта сістэмная праграма, яе нельга выдаліць."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Папка без назвы"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> адключана"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Старонка %1$d з %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Галоўны экран %1$d з %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Новая старонка галоўнага экрана"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Вітаем"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Скапір. значкі сваіх праграм"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Імпартаваць значкі і папкі з вашых старых Галоўных экранаў?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"СКАПІРАВАЦЬ ЗНАЧКІ"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ПАЧАЦЬ НАНАВА"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Шпалеры, віджэты і налады"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Дакраніцеся да любога месца ў фоне экрана і ўтрым. яго, каб дапасаваць"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ЗРАЗУМЕЛА"</string>
     <string name="folder_opened" msgid="94695026776264709">"Папка адкрыта, <xliff:g id="WIDTH">%1$d</xliff:g> на <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Дакраніцеся, каб закрыць папку"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Дакраніцеся, каб захаваць новую назву"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Краніце, каб закрыць папку"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Краніце, каб захаваць новую назву"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Папка закрыта"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Папка перайменавана ў <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Шпалеры"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Налады"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Адключаная адміністратарам"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Дазволіць паварот"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Агляд"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Дазволіць паварот галоўнага экрана"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Пры павароце тэлефона"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Бягучая налада дысплэя не прадугледжвае паварот"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Невядома"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Выдаліць"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Шукаць"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Паменшыць шырыню"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Паменшыць вышыню"</string>
     <string name="widget_resized" msgid="9130327887929620">"Памеры віджэта зменены на: шырыня <xliff:g id="NUMBER_0">%1$s</xliff:g>, вышыня <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Ярлыкі"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"Ярлыкі (<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>) для <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 5de4747..2c6d3d4 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"Няма достъп до прекия път"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Докоснете и задръжте за избор на приспособление."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Докоснете двукратно и задръжте за избор на приспособление или използвайте персонализирани действия."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Търсене в приложенията…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина %1$d и височина %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Търсене в приложенията"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Приложенията се зареждат…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Няма намерени приложения, съответстващи на „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Отваряне на <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Търсене на още приложения"</string>
     <string name="out_of_space" msgid="4691004494942118364">"На този начален екран няма повече място."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Няма повече място в областта с любимите"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Приложения"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Списък с приложения"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Начало"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Премахване"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Деинсталиране"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Информация за приложението"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Премахване"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталиране"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Данни за прилож."</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталиране на преки пътища"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Разрешава на приложението да добавя преки пътища без намеса на потребителя."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"четене на настройките и преките пътища в Начало"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Настройване"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Това е системно приложение и не може да се деинсталира."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Папка без име"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Деактивирахте <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Страница %1$d от %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Начален екран %1$d от %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Нова страница на началния екран"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Добре дошли"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Икони на прилож. ви: Копиране"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Да се импортират ли иконите и папките от старите ви начални екрани?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"КОПИРАНЕ НА ИКОНИТЕ"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"СТАРТИРАНЕ ОТНАЧАЛО"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Тапети, приспособления и настройки"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Докоснете и задръжте фона за персонализиране"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"РАЗБРАХ"</string>
     <string name="folder_opened" msgid="94695026776264709">"Папката е отворена – <xliff:g id="WIDTH">%1$d</xliff:g> на <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Докоснете, за да затворите папката"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Докоснете, за да запазите новото име"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Докоснете, за да затворите папката"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Докоснете, за да запазите новото име"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Папката бе затворена"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Папката е преименувана на „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Папка: „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Настройки"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Деактивирано от администратора ви"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Разрешаване на завъртането"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Общ преглед"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Разрешаване на завъртането на началния екран"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"При завъртане на телефона"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Текущата настройка на екрана не разрешава завъртане"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Няма информация"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Премахване"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Търсене"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Намаляване на ширината"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Намаляване на височината"</string>
     <string name="widget_resized" msgid="9130327887929620">"Приспособлението е преоразмерено към ширина <xliff:g id="NUMBER_0">%1$s</xliff:g> и височина <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Преки пътища"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> преки пътища за <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index 8a7f517..d5108e9 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"শর্টকাটগুলি অনুপলব্ধ"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"একটি উইজেট তুলতে তা স্পর্শ করে ধরে রাখুন৷"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"কোনো উইজেট বেছে নিতে দুবার-আলতো চেপে ধরে থাকুন অথবা কাস্টম ক্রিয়াগুলি ব্যবহার করুন৷"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"অ্যাপ্লিকেশানগুলি অনুসন্ধান করুন..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%2$d উচ্চতা অনুযায়ী %1$d প্রস্থ"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"অ্যাপ্লিকেশানগুলি অনুসন্ধান করুন"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"অ্যাপ্লিকেশানগুলি লোড হচ্ছে..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" এর সাথে মেলে এমন কোনো অ্যাপ্লিকেশান পাওয়া যায়নি"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> এ যান"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"আরো অ্যাপ্লিকেশানের জন্য অনুসন্ধান করুন"</string>
     <string name="out_of_space" msgid="4691004494942118364">"এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"পছন্দসই ট্রে-তে আর কোনো জায়গা নেই"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"অ্যাপ্লিকেশানগুলি"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"অ্যাপ্লিকেশানগুলির তালিকা"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"হোম"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"সরান"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"আনইনস্টল করুন"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"অ্যাপ্লিকেশানের তথ্য"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"সরান"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"আনইনস্টল করুন"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"অ্যাপ্লিকেশানের তথ্য"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"শর্টকাটগুলি ইনস্টল করে"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"একটি অ্যাপ্লিকেশানকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি যোগ করার অনুমতি দেয়৷"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"হোম সেটিংস এবং শর্টকাটগুলি পড়ে"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"সেটআপ"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"এটি একটি সিস্টেম অ্যাপ্লিকেশান এবং আনইনস্টল করা যাবে না৷"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"নামবিহীন ফোল্ডার"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> অক্ষম করা হয়েছে"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$dটির মধ্যে %1$dটি পৃষ্ঠা"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dটির %1$d নম্বর হোম স্ক্রীন"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"নতুন হোম স্ক্রীনের পৃষ্ঠা"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"স্বাগতম"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"আপনার অ্যাপ্লিকেশান আইকনগুলি কপি করুন"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"আপনার পুরানো হোম স্ক্রীন থেকে আইকন এবং ফোল্ডারগুলি আমদানি করবেন?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"আইকনগুলি কপি করুন"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"নতুন করে শুরু করুন"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"ওয়ালপেপার, উইজেট এবং সেটিংস"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"কাস্টমাইজ করার জন্য পটভূমি স্পর্শ করে ধরে থাকুন"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"বুঝেছি"</string>
     <string name="folder_opened" msgid="94695026776264709">"ফোল্ডার খোলা হয়েছে, <xliff:g id="WIDTH">%1$d</xliff:g> বাই <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"ফোল্ডার বন্ধ করতে স্পর্শ করুন"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"পুনঃনামকরণ সংরক্ষণ করতে স্পর্শ করুন"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"ফোল্ডার বন্ধ করতে আলতো চাপ দিন"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"পুনঃনামকরণ সংরক্ষণ করতে আলতো চাপ দিন"</string>
     <string name="folder_closed" msgid="4100806530910930934">"ফোল্ডার বন্ধ করা হয়েছে"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"ফোল্ডারের নাম পাল্টে <xliff:g id="NAME">%1$s</xliff:g> করা হয়েছে"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ওয়ালপেপারগুলি"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"সেটিংস"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"আপনার প্রশাসক দ্বারা অক্ষম করা হয়েছে"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"ঘূর্ণনের অনুমতি দিন"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"এক নজরে"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"হোমস্ক্রীন ঘোরানোর অনুমতি দিন"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"যখন ফোনটি ঘোরানো হয়"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"বর্তমান প্রদর্শনের সেটিংস ঘোরানোর মঞ্জুরি দেয় না"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"অজানা"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"সরান"</string>
     <string name="abandoned_search" msgid="891119232568284442">"অনুসন্ধান"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"প্রস্থ কমান"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"উচ্চতা কমান"</string>
     <string name="widget_resized" msgid="9130327887929620">"উইজেটের আকার প্রস্থ <xliff:g id="NUMBER_0">%1$s</xliff:g> উচ্চতা <xliff:g id="NUMBER_1">%2$s</xliff:g> তে পরিবর্তন করা হয়েছে"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"শর্টকাট"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> এর <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>টি শর্টকার্ট"</string>
 </resources>
diff --git a/res/values-bs-rBA/strings.xml b/res/values-bs-rBA/strings.xml
index f25548a..579b95a 100644
--- a/res/values-bs-rBA/strings.xml
+++ b/res/values-bs-rBA/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Aplikacija nije dostupna"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Preuzeta aplikacija je onemogućena u sigurnom načinu rada"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Vidžeti su onemogućeni u sigurnom načinu rada."</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Prečica nije dostupna"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Dodirnite &amp; i držite da biste uzeli dodatak."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dvaput dodirnite &amp; i držite da biste uzeli vidžet ili koristite prilagođene radnje."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Pretraži aplikacije"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, visina %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pretraži aplikacije"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikacije se učitavaju…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nije pronađena nijedna aplikacija koja odgovara upitu \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Idite na uslugu <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pretraži više aplikacija"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Na ovom početnom ekranu nema više prostora."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora u ladici Favoriti"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacije"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Spisak aplikacija"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Tipka za početak"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Ukloni"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Ukloni"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Informacije o aplikaciji"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Ukloni"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Informacije o aplikaciji"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliraj prečice"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Dopušta aplikaciji dodavanje prečica bez posredovanja korisnika."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"čitaj postavke na početnom ekranu i prečice"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Postavljanje"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je sistemska aplikacija i ne može se deinstalirati."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Neimenovana fascikla"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućena"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Strana %1$d od %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Početni ekran %1$d od %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog ekrana"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Dobrodošli"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopirajte ikone aplikacije"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Uvoz ikone i foldera sa starih početnih ekrana?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPIRAJ IKONE"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"POČNITE ISPOČETKA"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Pozadine, vidžeti, &amp; amp; podešavanja"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Dodirnite &amp; i držite pozadinu za prilagođavanje"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"JASNO MI JE"</string>
     <string name="folder_opened" msgid="94695026776264709">"Fascikla je otvorena, (š) <xliff:g id="WIDTH">%1$d</xliff:g> (v) <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Dodirnite da biste zatvorili fasciklu"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Dodirnite da biste sačuvali promjenu imena"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Dodirnite da zatvorite folder"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da sačuvate promjenu imena"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Fascikla je zatvorena"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Ime fascikle je promijenjeno u <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Fascikla: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Postavke"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio vaš administrator"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Dopusti rotaciju"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Dozvoli rotiranje početnog ekrana"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon zarotira"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Trenutne postavke ekrana ne dozvoljavaju rotiranje"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Traži"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Smanji širinu"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Smanji visinu"</string>
     <string name="widget_resized" msgid="9130327887929620">"Veličina vidžeta je promijenjena na širinu <xliff:g id="NUMBER_0">%1$s</xliff:g> visinu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Prečice"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> prečica za aplikaciju <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 0777e64..4ba0498 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"L\'aplicació no està disponible."</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'aplicació que has baixat està desactivada al mode segur."</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"En Mode segur, els widgets estan desactivats."</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"La drecera no està disponible"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Mantén premut un widget per triar-lo."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Fes doble toc i mantén premut per seleccionar un widget o per utilitzar les accions personalitzades."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Cerca a les aplicacions…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d d\'amplada per %2$d d\'alçada"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Cerca a les aplicacions"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"S\'estan carregant les aplicacions..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"No s\'ha trobat cap aplicació que coincideixi amb <xliff:g id="QUERY">%1$s</xliff:g>"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Vés a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Cerca més aplicacions"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Ja no queda espai en aquesta pantalla d\'inici."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"No hi ha més espai a la safata Preferits."</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplicacions"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Llista d\'aplicacions"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Inici"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Suprimeix"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstal·la"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Informació de l\'aplicació"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Suprimeix"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstal·la"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Dades de l\'aplicació"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instal·la dreceres"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet que una aplicació afegeixi dreceres sense la intervenció de l\'usuari."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"llegeix la configuració i les dreceres de la pantalla d\'inici"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Configuració"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Aquesta aplicació és una aplicació del sistema i no es pot desinstal·lar."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sense nom"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"S\'ha desactivat <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Pàgina %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla d\'inici %1$d de %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Pàgina de la pantalla d\'inici nova"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Us donem la benvinguda"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Copiar les icones d\'aplicació"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Importar icones i carpetes de pantalles d\'inici anteriors?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPIA LES ICONES."</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"NOU COMENÇAMENT"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Fons de pantalla, widgets i configuració"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Mantén premut el fons per fer personalitzacions."</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"D\'ACORD"</string>
     <string name="folder_opened" msgid="94695026776264709">"S\'ha obert la carpeta, <xliff:g id="WIDTH">%1$d</xliff:g> per <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Toca per tancar la carpeta"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Toca per desar el canvi de nom"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Toca per tancar la carpeta"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Toca per desar el nom nou"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Carpeta tancada"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"S\'ha canviat el nom de la carpeta a <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fons de pantalla"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Configuració"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desactivada per l\'administrador"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Permet la rotació"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Visió general"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Permet la rotació de la pantalla d\'inici"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"En girar el telèfon"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"La configuració actual de la pantalla no permet la rotació"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconegut"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Suprimeix"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Cerca"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Redueix l\'amplada"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Redueix l\'alçada"</string>
     <string name="widget_resized" msgid="9130327887929620">"S\'ha canviat la mida del widget a l\'amplada <xliff:g id="NUMBER_0">%1$s</xliff:g> i l\'alçada <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Dreceres"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> dreceres per a l\'aplicació <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index baffa1b..373920c 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -26,20 +26,22 @@
     <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>
     <string name="safemode_widget_error" msgid="4863470563535682004">"V nouzovém režimu jsou widgety zakázány."</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Zkratka není k dispozici"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Widget vyberete dotykem a podržením."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dvojitým klepnutím a podržením vyberte widget, případně použijte vlastní akce."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Hledat v aplikacích…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"šířka %1$d, výška %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Hledat aplikace"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Načítání aplikací…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Dotazu „<xliff:g id="QUERY">%1$s</xliff:g>“ neodpovídají žádné aplikace"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Přejít na <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Vyhledat další aplikace"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Na této ploše již není místo."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Na panelu Oblíbené položky již není místo."</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplikace"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Seznam aplikací"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Plocha"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Odstranit"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Odinstalovat"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Informace o aplikaci"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Odstranit"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinstalovat"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Informace o aplikaci"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalace zástupce"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Umožňuje aplikaci přidat zástupce bez zásahu uživatele."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"čtení nastavení a odkazů plochy"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Nastavení"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Toto je systémová aplikace a nelze ji odinstalovat."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Složka bez názvu"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je zakázána"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Strana %1$d z %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Plocha %1$d z %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nová stránka plochy"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Vítejte"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Zkopírování ikon aplikací"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Chcete importovat ikony a složky ze svých starých ploch?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"ZKOPÍROVAT IKONY"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ZAČÍT S VÝCHOZÍM ROZVRŽENÍM"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Tapety, widgety a nastavení"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Pozadí můžete přizpůsobit klepnutím a podržením"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ROZUMÍM"</string>
     <string name="folder_opened" msgid="94695026776264709">"Složka otevřena, rozměry <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Dotykem složku zavřete"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Dotykem uložíte změnu názvu"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Klepnutím složku zavřete"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Klepnutím změnu názvu uložíte"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Složka je uzavřena"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Složka přejmenována na <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Složka: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Nastavení"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázáno administrátorem"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Povolit otáčení"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Přehled"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Povolit otáčení plochy"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Při otočení telefonu"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Aktuální nastavení displeje neumožňuje otáčení"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznámé"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstranit"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Hledat"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Snížit šířku"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Snížit výšku"</string>
     <string name="widget_resized" msgid="9130327887929620">"Velikost widgetu upravena: šířka <xliff:g id="NUMBER_0">%1$s</xliff:g>, výška <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Zkratky"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"Počet zkratek pro aplikaci <xliff:g id="APP_NAME">%2$s</xliff:g>: <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>"</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 13d0873..9304140 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -26,20 +26,22 @@
     <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>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets er deaktiveret i Beskyttet tilstand"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Genvejen er ikke tilgængelig"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Tryk på en widget, og hold den nede for at vælge."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Tryk to gange, og hold fingeren nede for at vælge en widget eller bruge tilpassede handlinger."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Søg i Apps…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d i bredden og %2$d i højden"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Søg i Apps"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Indlæser apps…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Der blev ikke fundet nogen apps, som matcher \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Gå til <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Søg efter flere apps"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Der er ikke mere plads på denne startskærm."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Der er ikke mere plads i bakken Foretrukne"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Liste med apps"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Startskærm"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Fjern"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Afinstaller"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Oplysninger om appen"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Fjern"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Afinstaller"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Oplysninger om appen"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installere genveje"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillader, at en app tilføjer genveje uden brugerens indgriben."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"læs indstillinger og genveje for startskærmen"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Konfigurer"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dette er en systemapp, som ikke kan afinstalleres."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Unavngiven mappe"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> er deaktiveret"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Side %1$d ud af %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Startskærm %1$d ud af %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Ny startskærm"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Velkommen"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopiér dine appikoner"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Vil du importere ikoner og mapper fra gamle startskærme?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPIÉR IKONER"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"START PÅ EN FRISK"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Baggrunde, widgets og indstillinger"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Tryk på baggrunden, og hold fingeren nede for at tilpasse den"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK, FORSTÅET"</string>
     <string name="folder_opened" msgid="94695026776264709">"Mappen er åben, <xliff:g id="WIDTH">%1$d</xliff:g> gange <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Tryk for at lukke mappen"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Tryk for at gemme det nye navn"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Tryk for at lukke mappen"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tryk for at gemme omdøbningen"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Mappen er lukket"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Mappen er omdøbt til <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Baggrunde"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Indstillinger"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Deaktiveret af din administrator"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Tillad rotation"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Oversigt"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Tillad rotation af startskærmen"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Når telefonen roteres"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Den aktuelle indstilling for visning tillader ikke rotation"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Ukendt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjern"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Søg"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Reducer bredden"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Reducer højden"</string>
     <string name="widget_resized" msgid="9130327887929620">"Størrelsen for widgetten er ændret til bredde <xliff:g id="NUMBER_0">%1$s</xliff:g> og højde <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Genveje"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> genveje til <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 9b66be3..e461d46 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"App nicht verfügbar"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Heruntergeladene App im abgesicherten Modus deaktiviert"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets im abgesicherten Modus deaktiviert"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Verknüpfung nicht verfügbar"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Zum Hinzufügen Widget berühren und halten"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Zum Hinzufügen auf Widget doppeltippen und gedrückt halten oder benutzerdefinierte Aktionen verwenden."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Apps suchen…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breit und %2$d hoch"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"In Apps suchen"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Apps werden geladen..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Keine Apps für \"<xliff:g id="QUERY">%1$s</xliff:g>\" gefunden"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Gehe zu <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Weitere Apps suchen"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Auf diesem Startbildschirm ist kein Platz mehr vorhanden."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ablage \"Favoriten\" ist voll."</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Liste der Apps"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Startseite"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Entfernen"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Deinstallieren"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"App-Info"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Entfernen"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstallieren"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"App-Details"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Verknüpfungen installieren"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ermöglicht einer App das Hinzufügen von Verknüpfungen ohne Eingreifen des Nutzers"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"Einstellungen und Verknüpfungen auf dem Startbildschirm lesen"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Einrichten"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dies ist eine Systemanwendung, die nicht deinstalliert werden kann."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Unbenannter Ordner"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> deaktiviert"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Seite %1$d von %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Startbildschirm %1$d von %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Neue Startbildschirmseite"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Hallo!"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"App-Symbole kopieren"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Symbole und Ordner alter Startbildschirme importieren?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"Symbole kopieren"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"Standardübersicht verwenden"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Hintergründe, Widgets &amp; Einstellungen"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Berühre und halte den Hintergrund, um ihn anzupassen."</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
     <string name="folder_opened" msgid="94695026776264709">"Ordner geöffnet, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Ordner durch Berühren schließen"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Umbenennung durch Berühren speichern"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Ordner zum Schließen antippen"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Neuen Namen zum Speichern antippen"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Ordner geschlossen"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Ordner umbenannt in <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Hintergründe"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Einstellungen"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Von deinem Administrator deaktiviert"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Drehung zulassen"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Übersicht"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Drehung des Startbildschirms zulassen"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Bei Drehung des Smartphones"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Die aktuelle \"Display\"-Einstellung verhindert eine Drehung der Anzeige"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Unbekannt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Entfernen"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Suchen"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Breite verringern"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Höhe verringern"</string>
     <string name="widget_resized" msgid="9130327887929620">"Größe des Widgets zu Breite <xliff:g id="NUMBER_0">%1$s</xliff:g> und Höhe <xliff:g id="NUMBER_1">%2$s</xliff:g> geändert"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Verknüpfungen"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> Verknüpfungen für <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 5a1ffc7..119232d 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"Η συντόμευση δεν είναι διαθέσιμη"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Αγγίξτε παρατεταμένα για να πάρετε ένα γραφ.στοιχ."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Πατήστε δύο φορές παρατεταμένα για επιλογή γραφικού στοιχείου ή χρήση προσαρμοσμένων ενεργειών."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Αναζήτηση εφαρμογών…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Πλάτος %1$d επί ύψος %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Αναζήτηση εφαρμογών"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Φόρτωση εφαρμογών…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Δεν βρέθηκαν εφαρμογές για το ερώτημα \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Μετάβαση σε <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Αναζήτηση περισσότερων εφαρμογών"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Δεν υπάρχει χώρος σε αυτήν την αρχική οθόνη."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Δεν υπάρχει επιπλέον χώρος στην περιοχή Αγαπημένα"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Εφαρμογές"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Λίστα εφαρμογών"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Αρχική οθόνη"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Κατάργηση"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Κατάργηση εγκατάστασης"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Πληροφορίες εφαρμογής"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Κατάργηση"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Απεγκατάσταση"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Πληροφορίες εφαρμογής"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"εγκατάσταση συντομεύσεων"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Επιτρέπει σε μια εφαρμογή την προσθήκη συντομεύσεων χωρίς την παρέμβαση του χρήστη."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"ανάγνωση ρυθμίσεων και συντομεύσεων αρχικής οθόνης"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Ρύθμιση"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Αυτή είναι μια εφαρμογή συστήματος και δεν είναι δυνατή η κατάργηση της εγκατάστασής της."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Φάκελος χωρίς όνομα"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> είναι απενεργοποιημένη"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Σελίδα %1$d από %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Αρχική οθόνη %1$d από %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Νέα σελίδα αρχικής οθόνης"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Καλώς ορίσατε"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Αντιγραφή εικονιδίων εφαρμογών"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Εισαγωγή εικονιδίων και φακέλων από παλιές αρχικές οθόνες;"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"ΑΝΤΙΓΡΑΦΗ ΕΙΚΟΝΙΔΙΩΝ"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ΝΕΑ ΕΝΑΡΞΗ"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Ταπετσαρίες, γραφικά στοιχεία και ρυθμίσεις"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Αγγίξτε παρατεταμένα το παρασκήνιο για προσαρμογή"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ΕΓΙΝΕ"</string>
     <string name="folder_opened" msgid="94695026776264709">"Άνοιγμα φακέλου, <xliff:g id="WIDTH">%1$d</xliff:g> επί <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Αγγίξτε για να κλείσετε τον φάκελο"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Αγγίξτε για να αποθηκεύσετε το νέο όνομα"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Πατήστε για να κλείσετε το φάκελο"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Πατήστε για να αποθηκεύσετε τη νέα ονομασία"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Ο φάκελος έκλεισε"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Ο φάκελος μετονομάστηκε σε <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Φάκελος: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ταπετσαρίες"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Ρυθμίσεις"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Απενεργοποιήθηκε από τον διαχειριστή σας"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Να επιτρέπεται η περιστροφή"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Επισκόπηση"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Να επιτρέπεται η περιστροφή της αρχικής οθόνης"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Όταν το τηλέφωνο περιστρέφεται"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Η τρέχουσα ρύθμιση οθόνης δεν επιτρέπει την περιστροφή"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Άγνωστο"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Κατάργηση"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Αναζήτηση"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"μείωση του πλάτους"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Μείωση του ύψους"</string>
     <string name="widget_resized" msgid="9130327887929620">"Έγινε προσαρμογή του μεγέθους του γραφικού στοιχείου σε <xliff:g id="NUMBER_0">%1$s</xliff:g> πλάτος και <xliff:g id="NUMBER_1">%2$s</xliff:g> ύψος"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Συντομεύσεις"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> συντομεύσεις για <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index dff24f8..44daaa8 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"App isn\'t available"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets disabled in Safe mode"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Touch &amp; hold to pick up a widget."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap &amp; hold to pick up a widget or use customised actions."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Search Apps…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Search Apps"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Go to <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Search for more apps"</string>
     <string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Apps list"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Remove"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Uninstall"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"App info"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"read Home settings and shortcuts"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Welcome"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Copy your app icons"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Import icons and folders from your old Home screens?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPY ICONS"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"START AFRESH"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Wallpapers, widgets, &amp; settings"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Touch &amp; hold background to customise"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"GOT IT"</string>
     <string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Touch to close folder"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Touch to save rename"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Folder closed"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Settings"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Allow rotation"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Overview"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Current display setting doesn\'t permit rotation"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Decrease width"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Decrease height"</string>
     <string name="widget_resized" msgid="9130327887929620">"Widget re-sized to width <xliff:g id="NUMBER_0">%1$s</xliff:g> height <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Short cuts"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> shortcuts for <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index dff24f8..44daaa8 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"App isn\'t available"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets disabled in Safe mode"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Touch &amp; hold to pick up a widget."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap &amp; hold to pick up a widget or use customised actions."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Search Apps…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Search Apps"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Go to <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Search for more apps"</string>
     <string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Apps list"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Remove"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Uninstall"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"App info"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"read Home settings and shortcuts"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Welcome"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Copy your app icons"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Import icons and folders from your old Home screens?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPY ICONS"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"START AFRESH"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Wallpapers, widgets, &amp; settings"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Touch &amp; hold background to customise"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"GOT IT"</string>
     <string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Touch to close folder"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Touch to save rename"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Folder closed"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Settings"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Allow rotation"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Overview"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Current display setting doesn\'t permit rotation"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Decrease width"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Decrease height"</string>
     <string name="widget_resized" msgid="9130327887929620">"Widget re-sized to width <xliff:g id="NUMBER_0">%1$s</xliff:g> height <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Short cuts"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> shortcuts for <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index dff24f8..44daaa8 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"App isn\'t available"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets disabled in Safe mode"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Shortcut isn\'t available"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Touch &amp; hold to pick up a widget."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap &amp; hold to pick up a widget or use customised actions."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Search Apps…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d wide by %2$d high"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Search Apps"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Go to <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Search for more apps"</string>
     <string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Apps list"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Remove"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Uninstall"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"App info"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Remove"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Uninstall"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"App info"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"install shortcuts"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Allows an app to add shortcuts without user intervention."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"read Home settings and shortcuts"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Disabled <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Welcome"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Copy your app icons"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Import icons and folders from your old Home screens?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPY ICONS"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"START AFRESH"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Wallpapers, widgets, &amp; settings"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Touch &amp; hold background to customise"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"GOT IT"</string>
     <string name="folder_opened" msgid="94695026776264709">"Folder opened, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Touch to close folder"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Touch to save rename"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Tap to close folder"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tap to save rename"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Folder closed"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Folder renamed to <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Settings"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Allow rotation"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Overview"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"When phone is rotated"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Current display setting doesn\'t permit rotation"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Decrease width"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Decrease height"</string>
     <string name="widget_resized" msgid="9130327887929620">"Widget re-sized to width <xliff:g id="NUMBER_0">%1$s</xliff:g> height <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Short cuts"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> shortcuts for <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 104cae6..4bd6c88 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -26,20 +26,22 @@
     <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>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets inhabilitados en modo seguro"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"El acceso directo no está disponible"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Mantén presionado el widget que desees elegir."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Presiona dos veces y mantén presionado para elegir un widget o usa una acción personalizada."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Buscar apps…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Buscar aplicaciones"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicaciones…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"No hay aplicaciones que coincidan con <xliff:g id="QUERY">%1$s</xliff:g>."</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Ir a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar más apps"</string>
     <string name="out_of_space" msgid="4691004494942118364">"No hay más espacio en esta pantalla principal."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"La bandeja de favoritos está llena."</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplicaciones"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Lista de apps"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Pantalla principal"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Eliminar"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalar"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Información de la aplicación"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Quitar"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Información de app"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación agregue accesos directos sin que el usuario intervenga."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"leer configuración y accesos directos de la pantalla principal"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Configuración"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta es una aplicación del sistema y no se puede desinstalar."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sin nombre"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Se inhabilitó <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla principal %1$d de %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nueva página en la pantalla principal"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Bienvenido"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Copiar íconos de aplicaciones"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"¿Importar íconos y carpetas de pant. principales antiguas?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPIAR ÍCONOS"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"EMPEZAR DE CERO"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Fondos, widgets y configuración"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Mantén presionado el fondo para personalizarlo"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ENTENDIDO"</string>
     <string name="folder_opened" msgid="94695026776264709">"Carpeta abierta, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Toca para cerrar la carpeta."</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Toca para guardar el nuevo nombre."</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Presiona para cerrar la carpeta"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Presiona para guardar el cambio de nombre"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Carpeta cerrada"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"El nombre de la carpeta se cambió a <xliff:g id="NAME">%1$s</xliff:g>."</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Configuración"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"El administrador inhabilitó esta función"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Permitir la rotación"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Recientes"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir la rotación de la pantalla principal"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Al girar el teléfono"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"La configuración actual no permite la rotación de la pantalla"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Reducir el ancho"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Reducir la altura"</string>
     <string name="widget_resized" msgid="9130327887929620">"Se cambió la dimensión del widget a <xliff:g id="NUMBER_0">%1$s</xliff:g> de ancho y <xliff:g id="NUMBER_1">%2$s</xliff:g> de alto."</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Accesos directos"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> accesos directos para <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 5256dda..a4fc620 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -26,20 +26,22 @@
     <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>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets inhabilitados en modo seguro"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Acceso directo no disponible"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Mantén pulsado el widget que quieras seleccionar."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toca dos veces y mantén pulsado el widget que quieras seleccionar o utiliza acciones personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Buscar aplicaciones…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Busca aplicaciones"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicaciones…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"No se han encontrado aplicaciones que contengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Ir a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar más aplicaciones"</string>
     <string name="out_of_space" msgid="4691004494942118364">"No queda espacio en la pantalla de inicio."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"La bandeja de favoritos está completa"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplicaciones"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Lista de aplicaciones"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Inicio"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Eliminar"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalar"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Información de la aplicación"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Quitar"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Datos de aplicación"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar accesos directos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que una aplicación añada accesos directos sin intervención del usuario."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"leer información de accesos directos y de ajustes de la pantalla de inicio"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Configuración"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta aplicación es del sistema y no se puede desinstalar."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sin nombre"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Se ha inhabilitado <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla de inicio %1$d de %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nueva página de pantalla de inicio"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Te damos la bienvenida"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Copiar iconos de aplicaciones"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"¿Importar iconos y carpetas de pantallas de inicio antiguas?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPIAR ICONOS"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"AJUSTES PREDETERMINADOS"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Fondos de pantalla, widgets y ajustes"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Mantén pulsado el fondo para personalizarlo"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ENTENDIDO"</string>
     <string name="folder_opened" msgid="94695026776264709">"Carpeta abierta, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Toca para cerrar la carpeta"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Toca para cambiar el nuevo nombre"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Toca para cerrar la carpeta"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Toca para guardar el nuevo nombre"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Carpeta cerrada"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Se ha cambiado el nombre de la carpeta a <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,12 +67,15 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Ajustes"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inhabilitada por el administrador"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Permitir rotación"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Visión general"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotación de la pantalla de inicio"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Al girar el teléfono"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"La configuración de pantalla actual no permite girar la pantalla"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
-    <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
+    <string name="abandoned_clean_this" msgid="7610119707847920412">"Quitar"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicación no está instalada"</string>
-    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"La aplicación de este icono no está instalada. Puedes eliminar el icono o buscar la aplicación e instalarla manualmente."</string>
+    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"La aplicación de este icono no está instalada. Puedes quitar el icono o buscar la aplicación e instalarla manualmente."</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"Descargando <xliff:g id="NAME">%1$s</xliff:g> (<xliff:g id="PROGRESS">%2$s</xliff:g> completado)"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"Esperando para instalar <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"Añadir a la pantalla de inicio"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Reducir ancho"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Reducir altura"</string>
     <string name="widget_resized" msgid="9130327887929620">"Se ha modificado el tamaño del widget a <xliff:g id="NUMBER_0">%1$s</xliff:g> de ancho y <xliff:g id="NUMBER_1">%2$s</xliff:g> de alto"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Accesos directos"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> accesos directos de <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index b9540a5..feb63ca 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Rakendus ei ole saadaval"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Allalaetud rakendus on turvarežiimis keelatud"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Turvarežiimis on vidinad keelatud"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Otsetee pole saadaval"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Vidina valimiseks vajutage ja hoidke seda all."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Topeltpuudutage ja hoidke vidina valimiseks või kohandatud toimingute kasutamiseks."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Otsimine rakendustest …"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lai ja %2$d kõrge"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Otsige rakendustest"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Rakenduste laadimine ..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Päringule „<xliff:g id="QUERY">%1$s</xliff:g>” ei vastanud ükski rakendus"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Mine: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Otsi rohkem rakendusi"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Sellel avaekraanil pole enam ruumi."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Salves Lemmikud pole rohkem ruumi"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Rakendused"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Rakenduste loend"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Avaekraan"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Eemalda"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalli"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Rakenduse teave"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Eemalda"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalli"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Rakenduse teave"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installi otseteed"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Võimaldab rakendusel lisada otseteid kasutaja sekkumiseta."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"loe avaekraani seadeid ja otseteid"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Seadistamine"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"See on süsteemirakendus ja seda ei saa desinstallida."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Nimetu kaust"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Rakendus <xliff:g id="APP_NAME">%1$s</xliff:g> on keelatud"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Leht %1$d/%2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Avaekraan %1$d/%2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Uus avaekraan"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Tere tulemast"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopeerige rakenduste ikoonid"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Kas importida vanade avaekraanide ikoonid ja kaustad?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPEERI IKOONID"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ALUSTA ALGUSEST"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Taustapildid, vidinad ja seaded"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Kohandamiseks puudutage ja hoidke tausta all"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"SELGE"</string>
     <string name="folder_opened" msgid="94695026776264709">"Kaust on avatud, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Puudutage kausta sulgemiseks"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Puudutage uue nime salvestamiseks"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Puudutage kausta sulgemiseks"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Puudutage ümbernimetamise salvestamiseks"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Kaust on suletud"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Kausta uus nimi: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustapildid"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Seaded"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Keelas administraator"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Luba pööramine"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Ülevaade"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Luba avaekraani pööramine"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Kui telefoni pööratakse"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Praegune kuvaseade ei luba pööramist"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Teadmata"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eemalda"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Otsing"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Vähenda laiust"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Vähenda kõrgust"</string>
     <string name="widget_resized" msgid="9130327887929620">"Vidina suurust muudeti. Laius: <xliff:g id="NUMBER_0">%1$s</xliff:g>. Kõrgus: <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Otseteed"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> otseteed rakenduse <xliff:g id="APP_NAME">%2$s</xliff:g> jaoks"</string>
 </resources>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index 8201dd8..76ef5c6 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Ez dago erabilgarri aplikazioa"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Deskargatutako aplikazioa modu seguruan desgaitu da"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgetak desgaitu egin dira modu seguruan"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Eduki ukituta widgeta aukeratzeko."</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Lasterbideak ez daude erabilgarri"</string>
+    <string name="long_press_widget_to_add" msgid="7699152356777458215">"Eduki sakatuta widgeta aukeratzeko."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Sakatu birritan eta eduki sakatuta widgeta aukeratzeko edo ekintza pertsonalizatuak erabiltzeko."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Bilatu aplikazioetan…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d zabal eta %2$d luze"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Bilatu aplikazioetan"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikazioak kargatzen…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Ez da aurkitu \"<xliff:g id="QUERY">%1$s</xliff:g>\" bilaketarekin bat datorren aplikaziorik"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Joan hona: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Bilatu aplikazio gehiago"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Hasierako pantaila honetan ez dago toki gehiago."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ez dago toki gehiago Gogokoak erretiluan"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplikazioak"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Aplikazioen zerrenda"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Hasiera"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Kendu"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalatu"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Aplikazioaren informazioa"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Kendu"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalatu"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Aplikazioaren datuak"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalatu lasterbideak"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Erabiltzaileak ezer egin gabe lasterbideak gehitzea baimentzen die aplikazioei."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"Irakurri hasierako ezarpenak eta lasterbideak"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Konfigurazioa"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Sistema-aplikazioa da hau eta ezin da desinstalatu."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Izenik gabeko karpeta"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desgaituta dago"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d/%2$d orria"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d/%2$d hasierako pantaila"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Hasierako pantailaren orri berria"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Ongi etorri"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopiatu aplikazioen ikonoak"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Ikonoak eta karpetak aurreko hasierako pantailatik inportatu?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPIATU IKONOAK"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"HASI HUTSETIK"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Horma-paperak, widgetak eta ezarpenak"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Pertsonalizatzeko, eduki ukituta atzeko planoa"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ADOS"</string>
     <string name="folder_opened" msgid="94695026776264709">"Karpeta ireki da: <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Karpeta ixteko, uki ezazu"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Karpetaren izen berria gordetzeko, uki ezazu"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Karpeta ixteko, sakatu hau"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Izen berria gordetzeko, sakatu hau"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Karpeta itxi da"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Karpetari <xliff:g id="NAME">%1$s</xliff:g> izena eman zaio"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Karpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Horma-paperak"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Ezarpenak"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratzaileak desgaitu du"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Baimendu biratzeko aukera"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Ikuspegi orokorra"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Baimendu hasierako pantaila biratzea"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefonoa biratzen denean"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Uneko pantaila-ezarpenak ez du onartzen ikuspegia biratzea"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Ezezaguna"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Kendu"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Bilatu"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Txikitu zabalera"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Txikitu altuera"</string>
     <string name="widget_resized" msgid="9130327887929620">"Aldatu da widgetaren tamaina. Zabalera: <xliff:g id="NUMBER_0">%1$s</xliff:g>. Altuera: <xliff:g id="NUMBER_1">%2$s</xliff:g>."</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Lasterbideak"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> aplikazioaren <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> lasterbide"</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 73413dc..95675a7 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"میان‌بر دردسترس نیست"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"برای انتخاب ابزارک لمس کنید و نگه دارید."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"برای انتخاب یک ابزارک، دو ضربه سریع بزنید و نگه‌دارید یا از اقدامات سفارشی استفاده کنید."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"جستجوی برنامه‌ها…‏‏"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏%1$d عرض در %2$d طول"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"جستجوی برنامه‌ها"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"در حال بارگیری برنامه‌ها..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"هیچ برنامه‌ای مطابق با «<xliff:g id="QUERY">%1$s</xliff:g>» پیدا نشد"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"رفتن به <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"جستجوی برنامه‌های بیشتر"</string>
     <string name="out_of_space" msgid="4691004494942118364">"فضای بیشتری در این صفحه اصلی موجود نیست."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"فضای بیشتری در سینی موارد دلخواه وجود ندارد"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"برنامه‌ها"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"فهرست برنامه‌ها"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"صفحه اصلی"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"حذف"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"حذف نصب"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"اطلاعات برنامه"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"برداشتن"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"حذف نصب"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"اطلاعات برنامه"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"نصب میان‌برها"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"به برنامه اجازه می‌دهد میان‌برها را بدون دخالت کاربر اضافه کند."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"خواندن تنظیمات و میان‌برهای صفحه اصلی"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"تنظیم"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"این برنامه سیستمی است و حذف نصب نمی‌شود."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"پوشه بی‌نام"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> غیرفعال شد"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"‏صفحه %1$d از %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"‏صفحه اصلی %1$d از %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"صفحه اصلی جدید"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"خوش آمدید"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"کپی کردن نمادهای برنامه شما"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"نمادها و پوشه‌ها از صفحه‌های اصلی قدیمی شما وارد شوند؟"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"کپی نمادها"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"شروع تازه"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"کاغذدیواری‌ها، ابزارک‌ها و تنظیمات"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"برای سفارشی کردن، پس‌زمینه را لمس کنید و نگه‌دارید"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"متوجه شدم"</string>
     <string name="folder_opened" msgid="94695026776264709">"پوشه باز شده، <xliff:g id="WIDTH">%1$d</xliff:g> در <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"برای بستن پوشه لمس کنید"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"برای ذخیره تغییر نام لمس کنید"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"برای بستن پوشه، ضربه بزنید"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"برای ذخیره تغییر نام، ضربه بزنید"</string>
     <string name="folder_closed" msgid="4100806530910930934">"پوشه بسته شد"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"نام پوشه به <xliff:g id="NAME">%1$s</xliff:g> تغییر کرد"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"پوشه: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"کاغذدیواری‌ها"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"تنظیمات"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"توسط سرپرست سیستم غیرفعال شده است"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"چرخش مجاز است"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"نمای کلی"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"امکان دادن به چرخش صفحه اصلی"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"وقتی تلفن چرخانده می‌شود"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"تنظیم نمایشگر کنونی اجازه چرخش نمی‌دهد"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"نامشخص"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"حذف"</string>
     <string name="abandoned_search" msgid="891119232568284442">"جستجو"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"کاهش عرض"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"کاهش ارتفاع"</string>
     <string name="widget_resized" msgid="9130327887929620">"اندازه ابزارک به عرض <xliff:g id="NUMBER_0">%1$s</xliff:g> ارتفاع <xliff:g id="NUMBER_1">%2$s</xliff:g> تغییر کرد"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"میان‌برها"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> میانبر برای <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 52a47c7..40e6d96 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Sovellus ei ole käytettävissä"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Ladattu sovellus poistettiin käytöstä suojatussa tilassa"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgetit poistettu käytöstä vikasietotilassa"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Pikakuvake ei ole käytettävissä."</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Valitse widget painamalla sitä pitkään."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Valitse widget tai käytä muokattuja toimintoja kaksoisnapauttamalla ja painamalla kohdetta pitkään."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Hae sovelluksia…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Leveys: %1$d, korkeus: %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Sovellushaku"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Ladataan sovelluksia…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"”<xliff:g id="QUERY">%1$s</xliff:g>” ei palauttanut sovelluksia."</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Siirry: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Hae lisää sovelluksia"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Tässä aloitusruudussa ei ole enää tilaa."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Suosikit-valikossa ei ole enää tilaa"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Sovellukset"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Sovellusluettelo"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Aloitusruutu"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Poista kuvake"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Poista asennus"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Sovelluksen tiedot"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Poista"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Poista asennus"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Sovelluksen tiedot"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"asenna pikakuvakkeita"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Antaa sovelluksen lisätä pikakuvakkeita itsenäisesti ilman käyttäjän valintaa."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"lue aloitusruudun asetuksia ja pikakuvakkeita"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Asetus"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Tämä on järjestelmäsovellus, eikä sitä voi poistaa."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Nimetön kansio"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> poistettiin käytöstä"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Sivu %1$d / %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Aloitusruutu %1$d/%2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Uusi aloitusnäytön sivu"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Tervetuloa"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopioi sovelluskuvakkeet"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Tuodaanko kuvakkeet ja kansiot vanhoista aloitusruuduista?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPIOI KUVAKKEET"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ALOITA ALUSTA"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Taustakuvat, widgetit ja asetukset"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Muokkaa taustaa koskettamalla ja painamalla pitkään"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"SELVÄ"</string>
     <string name="folder_opened" msgid="94695026776264709">"Kansio avattu, koko <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Sulje kansio koskettamalla"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Tallenna uudella nimellä koskettamalla"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Sulje kansio koskettamalla."</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tallenna uusi nimi koskettamalla."</string>
     <string name="folder_closed" msgid="4100806530910930934">"Kansio on suljettu"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Kansion nimeksi vaihdettiin <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Kansio: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustakuvat"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Asetukset"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Järjestelmänvalvoja on poistanut toiminnon käytöstä."</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Salli kierto"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Yleiskatsaus"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Salli aloitusnäytön kiertäminen"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Kun puhelinta kierretään"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Nykyiset näyttöasetukset eivät salli näytön kiertämistä."</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Tuntematon"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Poista"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Haku"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Vähennä leveyttä"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Vähennä korkeutta"</string>
     <string name="widget_resized" msgid="9130327887929620">"Widgetin kokoa muutettiin. Sen leveys on nyt <xliff:g id="NUMBER_0">%1$s</xliff:g> ja korkeus <xliff:g id="NUMBER_1">%2$s</xliff:g>."</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Pikakuvakkeet"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"Sovelluksella <xliff:g id="APP_NAME">%2$s</xliff:g> on <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> pikakuvaketta."</string>
 </resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 77b11f1..01d6b27 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -26,20 +26,22 @@
     <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>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets désactivés en mode sans échec"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Le raccourci n\'est pas disponible"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Maintenez un doigt sur le widget pour l\'ajouter."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Touchez 2x un widget et maintenez doigt dessus pour l’ajouter ou utiliser des actions personnalisées"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Rechercher des applications..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largeur sur %2$d de hauteur"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Rechercher des applications"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Chargement des applications en cours..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Aucune application trouvée correspondant à « <xliff:g id="QUERY">%1$s</xliff:g> »"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Aller à « <xliff:g id="QUERY">%1$s</xliff:g> »"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Rechercher plus d\'applications"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Pas d\'espace libre sur l\'écran d\'accueil."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Il n\'y a plus d\'espace dans la zone des favoris"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Applications"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Liste des applications"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Accueil"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Supprimer"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Désinstaller"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Détails de l\'application"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Supprimer"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Désinstaller"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Détails de l\'appli"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permet à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"lire les paramètres et les raccourcis de la page d\'accueil"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Configuration"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Impossible de désinstaller cette application, car il s\'agit d\'une application système."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Dossier sans nom"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> est désactivée"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d sur %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Écran d\'accueil %1$d sur %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nouvelle page d\'écran d\'accueil"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Bienvenue"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Copier les icônes de vos applis"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Importer les icônes et dossiers des anciens écrans d\'accueil?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPIER LES ICÔNES"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"DISPOSITION PAR DÉFAUT"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Fonds d\'écran, widgets et paramètres"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Maintenez le doigt sur le fond d\'écran pour personnaliser"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"J\'ai compris"</string>
     <string name="folder_opened" msgid="94695026776264709">"Dossier ouvert, <xliff:g id="WIDTH">%1$d</xliff:g> par <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Toucher pour fermer le dossier"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Toucher pour enregistrer le nouveau nom"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Touchez pour fermer le dossier"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Touchez pour enregistrer le nouveau nom"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Dossier fermé"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Nouveau nom du dossier : <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Paramètres"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Cette fonction est désactivée par votre administrateur"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Permettre la rotation"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Présentation"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Autoriser la rotation de l\'écran d\'accueil"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Lorsque vous faites pivoter le téléphone"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Le mode d\'affichage actuel ne permet pas le pivotement"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Supprimer"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Rechercher"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Diminuer la largeur"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Diminuer la hauteur"</string>
     <string name="widget_resized" msgid="9130327887929620">"Le widget a été redimensionné (largeur : <xliff:g id="NUMBER_0">%1$s</xliff:g>, hauteur : <xliff:g id="NUMBER_1">%2$s</xliff:g>)"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Raccourcis"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> raccourcis pour <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index b9f90bc..f42748c 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -26,20 +26,22 @@
     <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>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Les widgets sont désactivés en mode sécurisé."</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Raccourci non disponible"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"App. de manière prolongée pour sélectionner widget."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Appuyez 2 fois et maintenez la pression pour sélectionner widget ou utilisez actions personnalisées."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d x %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Rechercher des applications…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largeur et %2$d de hauteur"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Rechercher dans les applications"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Chargement des applications en cours…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Aucune application ne correspond à la requête \"<xliff:g id="QUERY">%1$s</xliff:g>\"."</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Accéder à <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Rechercher plus d\'applications"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Pas d\'espace libre sur cet écran d\'accueil."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Plus d\'espace disponible dans la zone de favoris."</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Applications"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Liste d\'applications"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Accueil"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Supprimer"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Désinstaller"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Informations sur l\'application"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Supprimer"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Désinstaller"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Infos sur l\'appli"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installer des raccourcis"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permettre à une application d\'ajouter des raccourcis sans l\'intervention de l\'utilisateur"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"lire les paramètres et les raccourcis de l\'écran d\'accueil"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Configuration"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Impossible de désinstaller cette application, car il s\'agit d\'une application système."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Dossier sans nom"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> est désactivé."</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d sur %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Écran d\'accueil %1$d sur %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nouvelle page d\'écran d\'accueil"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Bienvenue"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Copier les icônes de vos applis"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Importer les icônes et dossiers des anciens écrans d\'accueil ?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPIER LES ICÔNES"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"DISPOSITION PAR DÉFAUT"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Fonds d\'écran, widgets et paramètres"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Appuyez de manière prolongée sur l\'arrière-plan pour le personnaliser."</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
     <string name="folder_opened" msgid="94695026776264709">"Dossier ouvert, <xliff:g id="WIDTH">%1$d</xliff:g> par <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Appuyez pour fermer le dossier."</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Appuyez pour enregistrer le nouveau nom."</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Appuyez pour fermer le dossier."</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Appuyez pour enregistrer le nouveau nom du dossier."</string>
     <string name="folder_closed" msgid="4100806530910930934">"Dossier fermé"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Nouveau nom du dossier : <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Dossier \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Paramètres"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Désactivé par votre administrateur"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Autoriser la rotation"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Vue d\'ensemble"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Autoriser la rotation de l\'écran d\'accueil"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Lorsque vous faites pivoter le téléphone"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Le paramètre d\'affichage actuel n\'autorise pas la rotation."</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Supprimer"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Rechercher"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Diminuer la largeur"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Diminuer la hauteur"</string>
     <string name="widget_resized" msgid="9130327887929620">"Le widget a bien été redimensionné (largeur : <xliff:g id="NUMBER_0">%1$s</xliff:g>, hauteur : <xliff:g id="NUMBER_1">%2$s</xliff:g>)."</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Raccourcis"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> raccourcis pour <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index 91e1ac3..c03ecdc 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"A aplicación non está dispoñible"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"A aplicación que descargaches está desactivada no modo seguro"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Os widgets están desactivados no modo seguro"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"O atallo non está dispoñible"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Mantén premido un widget para seleccionalo."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toca dúas veces e mantén premido para seleccionar un widget ou utiliza accións personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Aplicacións de busca..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largo por %2$d de alto"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Aplicacións de busca"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicacións..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Non se atoparon aplicacións que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Ir a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Buscar máis aplicacións"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Non hai máis espazo nesta pantalla de inicio."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Non hai máis espazo na bandexa de favoritos"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplicacións"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Lista de aplicacións"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Inicio"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Eliminar"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalar"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Información da aplicación"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Eliminar"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Info. da aplicación"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atallos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a unha aplicación engadir atallos sen intervención do usuario."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"ler a configuración e os atallos da pantalla de inicio"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Configuración"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta aplicación é do sistema e non se pode desinstalar."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Cartafol sen nome"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Desactivouse <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Páxina %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla de inicio %1$d de %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova páxina da pantalla de inicio"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Dámosche a benvida"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Copiar iconas das aplicacións"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Queres importar as iconas e os cartafoles doutras pantallas de inicio?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPIAR ICONAS"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"COMEZAR DE CERO"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Fondos pantalla, widgets e configuración"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Mantén tocado o segundo plano para personalizar"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"DE ACORDO"</string>
     <string name="folder_opened" msgid="94695026776264709">"Abriuse o cartafol, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Toca para pechar o cartafol"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Toca para gardar o cambio de nome"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Toca fóra para pechar o cartafol"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Toca fóra para cambiar o nome do cartafol"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Pechouse o cartafol"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"O cartafol cambiou o nome a <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Configuración"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Función desactivada polo administrador"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Permitir xiro"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Visión xeral"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir xirar a pantalla de inicio"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Ao xirar o teléfono"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"A configuración de visualización actual non permite xirar a pantalla"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Descoñecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Reducir ancho"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Reducir altura"</string>
     <string name="widget_resized" msgid="9130327887929620">"Cambiouse o tamaño do widget polo ancho <xliff:g id="NUMBER_0">%1$s</xliff:g> e a altura <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Atallos"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> atallos para <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index 51ebdce..39a176c 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -22,24 +22,26 @@
     <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="work_folder_name" msgid="3753320833950115786">"કાર્યાલય"</string>
-    <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="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="shortcut_not_available" msgid="2536503539825726397">"શૉર્ટકટ ઉપલબ્ધ નથી"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"વિજેટ ચૂંટવા માટે ટચ કરો અને પકડી રાખો."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"વિજેટ ચૂંટવા અથવા કસ્ટમ ક્રિયાઓનો ઉપયોગ કરવા માટે બે વાર ટેપ કરો અને પકડી રાખો."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ઍપ્લિકેશનોમાં શોધો…"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"એપ્લિકેશનો લોડ કરી રહ્યું છે…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" થી મેળ ખાતી કોઈ એપ્લિકેશનો મળી નથી"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> પર જાઓ"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d પહોળાઈ X %2$d ઊંચાઈ"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"શોધ ઍપ્લિકેશનો"</string>
+    <string name="all_apps_loading_message" msgid="7557140873644765180">"ઍપ્લિકેશનો લોડ કરી રહ્યું છે…"</string>
+    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" થી મેળ ખાતી કોઈ ઍપ્લિકેશનો મળી નથી"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"વધુ ઍપ્લિકેશનો શોધો"</string>
     <string name="out_of_space" msgid="4691004494942118364">"આ હોમ સ્ક્રીન પર વધુ જગ્યા નથી."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"મનપસંદ ટ્રે પર વધુ જગ્યા નથી"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"એપ્લિકેશનો"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"ઍપ્લિકેશનોની સૂચિ"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"હોમ"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"દૂર કરો"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"અનઇન્સ્ટોલ કરો"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"એપ્લિકેશન માહિતી"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"દૂર કરો"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"અનઇન્સ્ટોલ કરો"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"ઍપ્લિકેશન માહિતી"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"શોર્ટકટ્સ ઇન્સ્ટોલ કરો"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"એપ્લિકેશનને વપરાશકર્તા હસ્તક્ષેપ વગર શોર્ટકટ્સ ઉમેરવાની મંજૂરી આપે છે."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"હોમ સેટિંગ્સ અને શોર્ટકટ્સ વાંચો"</string>
@@ -49,22 +51,15 @@
     <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ને ફોન કૉલ્સ કરવાની મંજૂરી નથી"</string>
     <string name="gadget_error_text" msgid="6081085226050792095">"વિજેટ લોડ કરવામાં સમસ્યા"</string>
     <string name="gadget_setup_text" msgid="8274003207686040488">"સેટઅપ"</string>
-    <string name="uninstall_system_app_text" msgid="4172046090762920660">"આ એક સિસ્ટમ એપ્લિકેશન છે અને અનઇન્સ્ટોલ કરી શકાતી નથી."</string>
+    <string name="uninstall_system_app_text" msgid="4172046090762920660">"આ એક સિસ્ટમ ઍપ્લિકેશન છે અને અનઇન્સ્ટોલ કરી શકાતી નથી."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"અનામી ફોલ્ડર"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> અક્ષમ કરી"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d માંથી %1$d પૃષ્ઠ"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d માંથી %1$d હોમ સ્ક્રીન"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"નવું હોમ સ્ક્રીન પૃષ્ઠ"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"સ્વાગત છે"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"તમારા એપ્લિકેશન આયકન્સને કૉપિ કરો"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"તમારી જૂની હોમ સ્ક્રીન્સથી આયકન્સ અને ફોલ્ડર્સને આયાત કરીએ?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"આયકન્સને કૉપિ કરો"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"નવેસરથી પ્રારંભ કરો"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"વૉલપેપર્સ, વિજેટ્સ અને સેટિંગ્સ"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"પૃષ્ઠભૂમિને કસ્ટમાઇઝ કરવા માટે ટચ કરો અને પકડી રાખો"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"સમજાઈ ગયું"</string>
     <string name="folder_opened" msgid="94695026776264709">"<xliff:g id="WIDTH">%1$d</xliff:g> બાય <xliff:g id="HEIGHT">%2$d</xliff:g> નું ફોલ્ડર ખોલ્યું"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"ફોલ્ડર બંધ કરવા માટે ટચ કરો"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"નામ બદલવાનું સાચવવા માટે ટચ કરો"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"ફોલ્ડર બંધ કરવા માટે ટૅપ કરો"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"નામ બદલવાનું સાચવવા માટે ટૅપ કરો"</string>
     <string name="folder_closed" msgid="4100806530910930934">"ફોલ્ડર બંધ કર્યું"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"ફોલ્ડરનું નામ બદલીને <xliff:g id="NAME">%1$s</xliff:g> કર્યું"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,12 +67,15 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"વૉલપેપર્સ"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"સેટિંગ્સ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"તમારા વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"પરિભ્રમણને મંજૂરી આપો"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"વિહંગાવલોકન"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"હોમ સ્ક્રીનને ફેરવવાની મંજૂરી આપો"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"જ્યારે ફોન ફેરવવામાં આવે ત્યારે"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"વર્તમાન પ્રદર્શન સેટિંગ ફેરવવાની પરવાનગી આપતી નથી"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"અજાણ્યો"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"દૂર કરો"</string>
     <string name="abandoned_search" msgid="891119232568284442">"શોધો"</string>
-    <string name="abandoned_promises_title" msgid="7096178467971716750">"આ એપ્લિકેશન ઇન્સ્ટોલ થયેલ નથી"</string>
-    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"આ આયકન માટેની એપ્લિકેશન ઇન્સ્ટોલ થયેલ નથી. તમે તેને દૂર કરી શકો છો અથવા એપ્લિકેશન માટે શોધ કરી અને તેને મેન્યુઅલી ઇન્સ્ટોલ કરી શકો છો."</string>
+    <string name="abandoned_promises_title" msgid="7096178467971716750">"આ ઍપ્લિકેશન ઇન્સ્ટોલ થયેલ નથી"</string>
+    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"આ આયકન માટેની ઍપ્લિકેશન ઇન્સ્ટોલ થયેલ નથી. તમે તેને દૂર કરી શકો છો અથવા ઍપ્લિકેશન માટે શોધ કરી અને તેને મેન્યુઅલી ઇન્સ્ટોલ કરી શકો છો."</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ડાઉનલોડ કરી રહ્યાં છે, <xliff:g id="PROGRESS">%2$s</xliff:g> પૂર્ણ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g>, ઇન્સ્ટૉલ થવાની રાહ જોઈ રહ્યું છે"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"હોમ સ્ક્રીન પર ઉમેરો"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"પહોળાઈ ઘટાડો"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"ઊંચાઈ ઘટાડો"</string>
     <string name="widget_resized" msgid="9130327887929620">"વિજેટનો આકાર બદલીને <xliff:g id="NUMBER_0">%1$s</xliff:g> પહોળાઈ <xliff:g id="NUMBER_1">%2$s</xliff:g> ઊંચાઈ કર્યો"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"શૉર્ટકટ્સ"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> માટે <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> શૉર્ટકટ"</string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 5d10d3a..f534e1e 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"शॉर्टकट उपलब्ध नहीं है"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"विजेट को चुनने के लिए स्‍पर्श करके रखें."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"कोई विजेट चुनने के लिए डबल टैप करके रखें या कस्‍टम कार्रवाइयां चुनें."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ऐप्स खोजें…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d चौड़ाई गुणा %2$d ऊंचाई"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ऐप्‍स खोजें"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"ऐप्स लोड हो रहे हैं..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" से मिलान करने वाला कोई ऐप नहीं मिला"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> पर जाएं"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"अधिक ऐप्लिकेशन खोजें"</string>
     <string name="out_of_space" msgid="4691004494942118364">"इस होम स्‍क्रीन पर स्थान शेष नहीं है."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"पसंदीदा ट्रे में और स्थान नहीं है"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"ऐप्लिकेशन"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"ऐप्लिकेशन सूची"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"होम"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"निकालें"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"अनइंस्टॉल करें"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"ऐप्लिकेशन की जानकारी"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"निकालें"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"अनइंस्टॉल करें"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"ऐप की जानकारी"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट इंस्‍टॉल करें"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ऐप्लिकेशन को उपयोगकर्ता के हस्‍तक्षेप के बिना शॉर्टकट जोड़ने देती है."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"होम सेटिंग और शॉर्टकट पढ़ें"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"सेटअप"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"यह एक सिस्टम ऐप्लिकेशन है और इसे अनइंस्टॉल नहीं किया जा सकता."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"अनामित फ़ोल्डर"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> अक्षम है"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"पृष्ठ %2$d में से %1$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"होम स्क्रीन %2$d में से %1$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"नया होम स्‍क्रीन पृष्‍ठ"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"स्वागत है"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"ऐप्स आइकन की प्रतिलिपि बनाएं"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"अपनी पुरानी होम स्क्रीन से आइकन और फ़ोल्डर आयात करें?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"आइकन की प्रतिलिपि बनाएं"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"फिर से शुरू करें"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"वॉलपेपर, शॉर्टकट और सेटिंग"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"पृष्ठभूमि कस्टमाइज़ करने के लिए स्पर्श करके रखें"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"समझ लिया"</string>
     <string name="folder_opened" msgid="94695026776264709">"फ़ोल्डर खोला गया, <xliff:g id="WIDTH">%1$d</xliff:g> गुणा <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"फ़ोल्‍डर बंद करने के लिए स्‍पर्श करें"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"बदला गया नाम सहेजने के लिए स्पर्श करें"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"फ़ोल्डर बंद करने के लिए टैप करें"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"नाम बदलना सहेजने के लिए टैप करें"</string>
     <string name="folder_closed" msgid="4100806530910930934">"फ़ोल्डर बंद किया गया"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"फ़ोल्डर का नाम बदलकर <xliff:g id="NAME">%1$s</xliff:g> किया गया"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"फ़ोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"सेटिंग"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपके व्यवस्थापक द्वारा अक्षम"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"रोटेशन की अनुमति दें"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"अवलोकन"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"होमस्क्रीन घुमाने की अनुमति दें"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"फ़ोन घुुमाए जाने पर"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"वर्तमान प्रदर्शन सेटिंग घुमाने की अनुमति नहीं देती"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"निकालें"</string>
     <string name="abandoned_search" msgid="891119232568284442">"खोजें"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"चौड़ाई घटाएं"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"ऊंचाई घटाएं"</string>
     <string name="widget_resized" msgid="9130327887929620">"विजेट का आकार बदलकर उसकी चौड़ाई <xliff:g id="NUMBER_0">%1$s</xliff:g> और ऊंचाई <xliff:g id="NUMBER_1">%2$s</xliff:g> कर दी गई"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"शॉर्टकट"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> के लिए <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> शॉर्टकट"</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index cdae4ed..b4fb50e 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Aplikacija nije dostupna"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Preuzeta aplikacija onemogućena je u Sigurnom načinu rada"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgeti su onemogućeni u Sigurnom načinu rada"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Prečac nije dostupan"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Dodirnite i držite kako biste podigli widget."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dodirnite dvaput i držite kako biste podigli widget ili pokušajte prilagođenim radnjama."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Pretraživanje aplikacija…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d širine i %2$d visine"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pretraži aplikacije"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Učitavanje aplikacija…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nema aplikacija podudarnih s upitom \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Idite na <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Traži više aplikacija"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Na ovom početnom zaslonu više nema mjesta."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora na traci Favoriti"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacije"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Popis aplikacija"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Početna"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Ukloni"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Deinstaliraj"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Informacije o aplikaciji"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Ukloni"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deinstaliraj"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Info o aplikaciji"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instaliranje prečaca"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji omogućuje dodavanje prečaca bez intervencije korisnika."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"čitanje postavki početnog zaslona i prečaca"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Postavljanje"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je aplikacija sustava i ne može se ukloniti."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Neimenovana mapa"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> onemogućena"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Stranica %1$d od %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Početni zaslon %1$d od %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog zaslona"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Dobro došli"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopiranje ikona aplikacija"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Želite li uvesti ikone i mape sa starih početnih zaslona?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPIRAJ IKONE"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"POKRENI NOVO"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Pozadine, widgeti i postavke"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Dodirnite i zadržite pozadinu radi prilagodbe"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"SHVAĆAM"</string>
     <string name="folder_opened" msgid="94695026776264709">"Mapa je otvorena, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Dodirnite da biste zatvorili mapu"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Dodirnite da biste spremili preimenovanje"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Dodirnite da biste zatvorili mapu"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Dodirnite da biste spremili promijenjeni naziv"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Mapa je zatvorena"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Mapa je preimenovana u <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Postavke"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio administrator"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Dopusti rotaciju"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Dopusti zakretanje početnog zaslona"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Kada se telefon zakrene"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Trenutačna postavka zaslona ne dopušta zakretanje"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Traži"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Smanjenje širine"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Smanjenje visine"</string>
     <string name="widget_resized" msgid="9130327887929620">"Širina widgeta promijenjena je na <xliff:g id="NUMBER_0">%1$s</xliff:g>, a visina na <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Prečaci"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"Prečaca za aplikaciju <xliff:g id="APP_NAME">%2$s</xliff:g>: <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>"</string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 400a426..279749b 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Az alkalmazás nem érhető el"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"A letöltött alkalmazás Csökkentett módban ki van kapcsolva"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"A modulok ki vannak kapcsolva Csökkentett módban"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"A gyorsparancs nem áll rendelkezésre"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Modul felvételéhez érintse meg, és tartsa lenyomva"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Modul mozgatásához koppintson rá duplán és tartsa lenyomva, vagy használjon egyéni műveleteket."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Alkalmazások keresése…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d széles és %2$d magas"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Alkalmazások keresése"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Alkalmazások betöltése…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Egy alkalmazás sem található a(z) „<xliff:g id="QUERY">%1$s</xliff:g>” lekérdezésre."</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Keresse fel ezt: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"További alkalmazások keresése"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Nincs több hely ezen a kezdőképernyőn."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Nincs több hely a Kedvencek tálcán"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Alkalmazások"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Alkalmazások listája"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Főoldal"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Eltávolítás"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Törlés"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Alkalmazásinformáció"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Törlés"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Eltávolítás"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Alkalmazásinformáció"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"parancsikonok telepítése"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Lehetővé teszi egy alkalmazás számára, hogy felhasználói beavatkozás nélkül adjon hozzá parancsikonokat."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"Főoldal beállításainak és parancsikonjainak beolvasása"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Beállítás"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ez egy rendszeralkalmazás, és nem lehet eltávolítani."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Névtelen mappa"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> letiltva"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d/%1$d. oldal"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d/%1$d. kezdőképernyő"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Új kezdőképernyő oldal"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Üdvözöljük!"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Alkalmazásikonok másolása"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Importálja ikonjait és mappáit régi kezdőképernyőiről?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"IKONOK MÁSOLÁSA"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"TELJESEN ÚJ"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Háttérképek, modulok és beállítások"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Érintse meg és tartsa lenyomva a személyre szabáshoz"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"MEGÉRTETTEM"</string>
     <string name="folder_opened" msgid="94695026776264709">"Mappa megnyitva – szélesség: <xliff:g id="WIDTH">%1$d</xliff:g>; magasság: <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Érintse meg a mappa bezárásához"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Érintse meg az átnevezés mentéséhez"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Érintse meg a mappa bezárásához"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Koppintson ide az átnevezés mentéséhez"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Mappa lezárva"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"A mappa új neve: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Háttérképek"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Beállítások"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"A rendszergazda letiltotta"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Elforgatás engedélyezése"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Áttekintés"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"A kezdőképernyő elforgatásának engedélyezése"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"A telefon elforgatásakor"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"A jelenlegi kijelzőbeállítások nem teszik lehetővé az elforgatást"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Ismeretlen"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eltávolítás"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Keresés"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Szélesség csökkentése"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Magasság csökkentése"</string>
     <string name="widget_resized" msgid="9130327887929620">"Modul átméretezve <xliff:g id="NUMBER_0">%1$s</xliff:g> szélességre és <xliff:g id="NUMBER_1">%2$s</xliff:g> magasságra"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Gyorsparancsok"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> gyorsparancs a(z) <xliff:g id="APP_NAME">%2$s</xliff:g> számára"</string>
 </resources>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 79bc330..c7401a9 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"Դյուրանցումն անհասանելի է"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Հպեք և պահեք՝ վիջեթն ընտրելու համար:"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Կրկնակի հպեք և պահեք՝ վիջեթ ավելացնելու համար կամ օգտվեք հարմարեցրած գործողություններից:"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Հավելվածների որոնում…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Լայնությունը՝ %1$d, բարձրությունը՝ %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Հավելվածների որոնում"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Հավելվածների բեռնում…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"«<xliff:g id="QUERY">%1$s</xliff:g>» հարցմանը համապատասխանող հավելվածներ չեն գտնվել"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Գնալ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Որոնել այլ հավելվածներ"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Այլևս տեղ չկա այս հիմնական էկրանին:"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ընտրյալների ցուցակում այլևս ազատ տեղ չկա"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Ծրագրեր"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Հավելվածների ցանկ"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Հիմնական"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Հեռացնել"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Ապատեղադրել"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Ծրագրի տեղեկություններ"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Հեռացնել"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Հեռացնել"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Հավելվածի տվյալներ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"տեղադրել դյուրանցումներ"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ծրագրին թույլ է տալիս ավելացնել դյուրանցումներ՝ առանց օգտագործողի միջամտության:"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"կարդալ հիմնաէջի կարգավորումներն ու դյուրանցումները"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Կարգավորում"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Սա համակարգային ծրագիր է և չի կարող ապատեղադրվել:"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Անանուն պանակ"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածն անջատված է"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Էջ %1$d՝ %2$d-ից"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Հիմնական էկրան %1$d` %2$d-ից"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Հիմնական էկրանի նոր էջ"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Բարի գալուստ"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Պատճենել ձեր ծրագրի պատկերակները"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Ներմուծե՞լ պատկերակները և պանակները ձեր նախկին Հիմնական էկրանից"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"ՊԱՏՃԵՆԵԼ ՊԱՏԿԵՐԱԿՆԵՐԸ"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ՄԵԿՆԱՐԿԵԼ ԸՍՏ ԿԱՆԽԱԴՐՎԱԾԻ"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Պաստառներ, վիջեթներ և կարգավորումներ"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Հարմարեցնելու համար հպեք և պահեք հետնաշերտի վրա"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ՀԱՍԿԱՆԱԼԻ Է"</string>
     <string name="folder_opened" msgid="94695026776264709">"Պանակը բաց է, <xliff:g id="WIDTH">%1$d</xliff:g>-ից <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Հպեք՝ պանակը փակելու համար"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Հպեք՝ վերանվանումը պահելու համար"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Հպեք՝ պանակը փակելու համար"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Հպեք՝ նոր անվանումը պահելու համար"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Պանակը փակ է"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Պանակը վերանվանվեց <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Պանակ՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Պաստառներ"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Կարգավորումներ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Անջատվել է ձեր ադմինիստրատորի կողմից"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Թույլ տալ պարբերական կրկնությունը"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Համատեսք"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Թույլ տալ հիմնական էկրանի պտտումը"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Հեռախոսը պտտելու դեպքում"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Ցուցադրման ընթացիկ կարգավորումներն արգելում են պտտումը"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Անհայտ է"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Հեռացնել"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Գտնել"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Նվազեցնել լայնությունը"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Նվազեցնել բարձրությունը"</string>
     <string name="widget_resized" msgid="9130327887929620">"Վիջեթի լայնությունը փոխվել է <xliff:g id="NUMBER_0">%1$s</xliff:g>-ի, իսկ բարձրությունը՝ <xliff:g id="NUMBER_1">%2$s</xliff:g>-ի"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Դյուրանցումներ"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> դյուրանցումներ <xliff:g id="APP_NAME">%2$s</xliff:g> հավելվածի համար"</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index a48c56f..00e8d63 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Aplikasi tidak tersedia"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplikasi yang diunduh dinonaktifkan dalam mode Aman"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widget dinonaktifkan dalam mode Aman"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Pintasan tidak tersedia"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Sentuh lama untuk memilih widget."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ketuk dua kalip &amp; tahan untuk mengambil widget atau menggunakan tindakan khusus."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Telusuri Aplikasi..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"lebar %1$d x tinggi %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Telusuri Apps"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Memuat Aplikasi..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Tidak ditemukan Aplikasi yang cocok dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Buka <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Telusuri aplikasi lainnya"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Tidak ada ruang lagi pada layar Utama ini."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Tidak ada ruang tersisa di baki Favorit"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplikasi"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Daftar aplikasi"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Layar Utama"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Hapus"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Copot pemasangan"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Info aplikasi"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Hapus"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Copot pemasangan"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Info aplikasi"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"memasang pintasan"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Mengizinkan aplikasi menambahkan pintasan tanpa campur tangan pengguna."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"membaca setelan dan pintasan layar Utama"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Siapkan"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini adalah aplikasi sistem dan tidak dapat dicopot pemasangannya."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Folder Tanpa Nama"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> dinonaktifkan"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Laman %1$d dari %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Layar utama %1$d dari %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Laman layar utama baru"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Selamat Datang"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Salin ikon aplikasi Anda"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Impor ikon dan folder dari layar Utama lama Anda?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"IKON SALIN"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"MULAI DARI AWAL"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Wallpaper, widget, &amp; setelan"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Sentuh &amp; tahan latar belakang untuk menyesuaikan"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"MENGERTI"</string>
     <string name="folder_opened" msgid="94695026776264709">"Folder dibuka, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Sentuh untuk menutup folder"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Sentuh untuk menyimpan ganti nama"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Ketuk untuk menutup folder"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Ketuk untuk menyimpan ganti nama"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Folder ditutup"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Folder diganti namanya menjadi <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpaper"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Setelan"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dinonaktifkan oleh admin"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Izinkan putaran"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Ringkasan"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Izinkan layar Utama diputar"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Saat ponsel diputar"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Setelan Tampilan Saat Ini tidak memungkinkan putaran"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Tidak dikenal"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Buang"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Telusuri"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Kurangi lebar"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Kurangi tinggi"</string>
     <string name="widget_resized" msgid="9130327887929620">"Widget diubah ukurannya menjadi lebar <xliff:g id="NUMBER_0">%1$s</xliff:g> tinggi <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Pintasan"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> pintasan untuk <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index 5e2a07a..7586ae3 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Forritið er ekki í boði"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Sótt forrit er óvirkt í öryggisstillingu"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Græjur eru óvirkar í öruggri stillingu"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Flýtileið er ekki tiltæk"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Haltu fingri á græju til að grípa hana."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ýttu tvisvar og haltu fingri á græju til að grípa hana eða notaðu sérsniðnar aðgerðir."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Leita í forritum…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d á breidd og %2$d á hæð"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Leita í forritum"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Hleður forrit…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Ekki fundust forrit sem samsvara „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Fara í <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Leita að fleiri forritum"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Ekki meira pláss á þessum heimaskjá."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ekki meira pláss í bakka fyrir uppáhald"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Forrit"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Forritalisti"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Heim"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Fjarlægja"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Eyða"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Upplýsingar um forrit"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Fjarlægja"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Fjarlægja"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Forritsupplýsingar"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"setja upp flýtileiðir"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Leyfir forriti að bæta við flýtileiðum án íhlutunar notanda."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"lesa stillingar og flýtileiðir heimaskjás"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Uppsetning"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Þetta er kerfisforrit sem ekki er hægt að fjarlægja."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Ónefnd mappa"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Óvirkt <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Síða %1$d af %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Heimaskjár %1$d af %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Ný síða á heimaskjá"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Komdu fagnandi"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Afritaðu forritatáknin þín"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Flytja inn tákn og möppur af eldri heimaskjáum?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"AFRITA TÁKN"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"BYRJA UPP Á NÝTT"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Veggfóður, græjur og stillingar"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Haltu fingri á bakgrunninum til að sérsníða hann"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ÉG SKIL"</string>
     <string name="folder_opened" msgid="94695026776264709">"Mappa opnuð, <xliff:g id="WIDTH">%1$d</xliff:g> sinnum <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Snertu til að loka möppunni"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Snertu til að staðfesta nýtt heiti"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Ýttu til að loka möppunni"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Ýttu til að vista breytt heiti"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Möppu lokað"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Heiti möppu breytt í <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Veggfóður"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Stillingar"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gert óvirkt af kerfisstjóra"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Leyfa snúning"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Yfirlit"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Leyfa snúning fyrir heimaskjá"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Þegar símanum er snúið"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Núverandi skjástilling leyfir ekki snúning"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Óþekkt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjarlægja"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Leita"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Minnka breidd"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Minnka hæð"</string>
     <string name="widget_resized" msgid="9130327887929620">"Stærð græju breytt í <xliff:g id="NUMBER_0">%1$s</xliff:g> á breidd og <xliff:g id="NUMBER_1">%2$s</xliff:g> á hæð"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Flýtileiðir"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> flýtileiðir fyrir <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index f41fcc7..b821222 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"App non disponibile"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'app scaricata è stata disattivata in modalità provvisoria"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widget disabilitati in modalità provvisoria"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"La scorciatoia non è disponibile"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Tocca e tieni premuto per scegliere un widget."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Tocca due volte e tieni premuto per scegliere un widget o per utilizzare azioni personalizzate."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Ricerca app…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d di larghezza per %2$d di altezza"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Cerca app"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Caricamento di app…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nessuna app trovata corrispondente a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Vai a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Cerca altre app"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Spazio nella schermata Home esaurito."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Spazio esaurito nella barra dei Preferiti"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"App"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Elenco di app"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Home page"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Rimuovi"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Disinstalla"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Informazioni app"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Rimuovi"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Disinstalla"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Informazioni app"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"aggiunta di scorciatoie"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Consente a un\'app di aggiungere scorciatoie automaticamente."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"lettura di impostazioni e scorciatoie in Home"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Configurazione"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Questa è un\'app di sistema e non può essere disinstallata."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Cartella senza nome"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"App <xliff:g id="APP_NAME">%1$s</xliff:g> disattivata"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d di %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Schermata Home %1$d di %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nuova pagina Schermata Home"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Benvenuto"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Copia le icone delle tue app"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Importare icone e cartelle dalle schermate Home precedenti?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPIA ICONE"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"RICOMINCIA"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Sfondi, widget e impostazioni"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Tocca lo sfondo e tieni premuto per personalizzare"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
     <string name="folder_opened" msgid="94695026776264709">"Cartella aperta, <xliff:g id="WIDTH">%1$d</xliff:g> per <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Tocca per chiudere la cartella"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Tocca per salvare nuovo nome"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Tocca per chiudere la cartella"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tocca per salvare il nuovo nome"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Cartella chiusa"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Nome della cartella sostituito con <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Cartella: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Sfondi"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Impostazioni"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disattivata dall\'amministratore"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Consenti rotazione"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Panoramica"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Consenti rotazione della schermata Home"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Con il telefono ruotato"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"L\'impostazione corrente del display non consente la rotazione"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Sconosciuto"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Rimuovi"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Cerca"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Riduci larghezza"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Riduci altezza"</string>
     <string name="widget_resized" msgid="9130327887929620">"Widget ridimensionato a larghezza <xliff:g id="NUMBER_0">%1$s</xliff:g>, altezza <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Scorciatoie"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> scorciatoie per <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 8d0dc2d..ffea51f 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"קיצור הדרך אינו זמין"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"גע נגיעה רציפה בווידג\'ט כדי לבחור בו."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"הקש פעמיים וגע נגיעה רציפה בווידג\'ט כדי לבחור בו, או השתמש בפעולות מותאמות אישית."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"חיפוש אפליקציות..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏רוחב %1$d על גובה %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"חפש אפליקציות"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"טוען אפליקציות…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"לא נמצאו אפליקציות התואמות ל-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"עבור אל <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"חפש אפליקציות נוספות"</string>
     <string name="out_of_space" msgid="4691004494942118364">"אין עוד מקום במסך דף הבית הזה."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"אין עוד מקום במגש המועדפים"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"אפליקציות"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"רשימת אפליקציות"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"דף הבית"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"הסר"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"הסר התקנה"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"פרטי אפליקציה"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"הסר"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"הסר התקנה"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"פרטי אפליקציה"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"התקן קיצורי דרך"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"מאפשר לאפליקציה להוסיף קיצורי דרך ללא התערבות המשתמש."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"קרא הגדרות וקיצורי דרך של דף הבית"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"הגדר"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"זוהי אפליקציית מערכת ולא ניתן להסיר את התקנתה."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"תיקיה ללא שם"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> מושבתת"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"‏דף %1$d מתוך %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"‏מסך דף הבית %1$d מתוך %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"מסך דף הבית חדש"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"ברוכים הבאים"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"העתקת סמלי האפליקציות שלך"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"האם לייבא סמלים ותיקיות ממסכי דף הבית הישנים שלך?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"העתק סמלים"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"התחל דף חדש"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"טפטים, ווידג\'טים והגדרות"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"גע והחזק ברקע לביצוע התאמה אישית"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"הבנתי"</string>
     <string name="folder_opened" msgid="94695026776264709">"תיקיה פתוחה, <xliff:g id="WIDTH">%1$d</xliff:g> על <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"גע כדי לסגור את התיקיה"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"גע כדי לשמור את שינוי השם"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"הקש כדי לסגור את התיקייה"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"הקש כדי לשמור שינוי שם"</string>
     <string name="folder_closed" msgid="4100806530910930934">"התיקיה נסגרה"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"שם התיקיה שונה ל-<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"תיקיה: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"טפטים"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"הגדרות"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"הושבת על ידי מנהל המערכת שלך"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"אפשרות סיבוב"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"סקירה"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"אפשרות סיבוב של מסך דף הבית"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"כאשר הטלפון מסובב"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"הגדרת התצוגה הנוכחית אינה מאפשרת סיבוב"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"לא ידוע"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"הסר"</string>
     <string name="abandoned_search" msgid="891119232568284442">"חפש"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"הקטן רוחב"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"הקטן גובה"</string>
     <string name="widget_resized" msgid="9130327887929620">"גודל הווידג\'ט שונה - רוחב <xliff:g id="NUMBER_0">%1$s</xliff:g> גובה <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"קיצורי דרך"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> קיצורי דרך עבור <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 69ae46e..8729aba 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"ショートカットは使用できません"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"ウィジェットを追加するには押し続けます。"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ダブルタップ後に押し続けてウィジェットを選択するか、カスタム操作を使用してください。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$dx%2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"アプリを検索…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"幅 %1$d、高さ %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"アプリを検索"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"アプリを読み込んでいます…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"「<xliff:g id="QUERY">%1$s</xliff:g>」に一致するアプリは見つかりませんでした"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>にアクセス"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"他のアプリを検索"</string>
     <string name="out_of_space" msgid="4691004494942118364">"このホーム画面に空きスペースがありません。"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"お気に入りトレイに空きスペースがありません"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"アプリ"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"アプリのリスト"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"ホーム"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"削除"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"アンインストール"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"アプリ情報"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"削除"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"アンインストール"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"アプリ情報"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ショートカットのインストール"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ユーザー操作なしでショートカットを追加することをアプリに許可します。"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"ホームの設定とショートカットの読み取り"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"セットアップ"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"このシステムアプリはアンインストールできません。"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"名前のないフォルダ"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」は無効です"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d/%2$dページ"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"ホーム画面: %1$d/%2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"新しいホーム画面ページ"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"ようこそ"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"アプリのアイコンをコピー"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"古いホーム画面からアイコンとフォルダをインポートしますか?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"アイコンをコピー"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"初期状態にリセットする"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"壁紙、ウィジェット、設定"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"カスタマイズするには背景を押し続けます"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
     <string name="folder_opened" msgid="94695026776264709">"フォルダが開いています。<xliff:g id="WIDTH">%1$d</xliff:g>x<xliff:g id="HEIGHT">%2$d</xliff:g>の大きさです"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"タップしてフォルダを閉じます"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"タップして名前の変更を保存します"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"タップしてフォルダを閉じます"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"タップして変更後の名前を保存します"</string>
     <string name="folder_closed" msgid="4100806530910930934">"フォルダは閉じています"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"フォルダの名前を「<xliff:g id="NAME">%1$s</xliff:g>」に変更しました"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"フォルダ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"壁紙"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"設定"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"管理者により無効にされています"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"回転を許可"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"概要"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"ホーム画面の回転を許可"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"スマートフォンが回転したとき"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"現在の [ディスプレイ] 設定では回転を使用できません"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"削除"</string>
     <string name="abandoned_search" msgid="891119232568284442">"検索"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"幅を狭くする"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"高さを低くする"</string>
     <string name="widget_resized" msgid="9130327887929620">"ウィジェットのサイズを幅<xliff:g id="NUMBER_0">%1$s</xliff:g>、高さ<xliff:g id="NUMBER_1">%2$s</xliff:g>に変更しました"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"ショートカット"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g>用の <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> 件のショートカット"</string>
 </resources>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index ea7ee74..1a200d7 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"მალსახმობი მიუწვდომელია"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"შეეხეთ და დააყოვნეთ ვიჯეტის ასარჩევად."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ორმაგად შეეხეთ და გეჭიროთ ვიჯეტის ასარჩევად ან მორგებული მოქმედებების გამოსაყენებლად."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"აპების ძიება…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"სიგრძე: %1$d, სიგანე: %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"აპების ძიება"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"აპები იტვირთება..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"„<xliff:g id="QUERY">%1$s</xliff:g>“-ის თანხვედრი აპები არ მოიძებნა"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"გადადი <xliff:g id="QUERY">%1$s</xliff:g>-ში"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"მეტი აპის პოვნა"</string>
     <string name="out_of_space" msgid="4691004494942118364">"ამ მთავარ ეკრანზე ადგილი აღარ არის."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"რჩეულების თაროზე ადგილი არ არის"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"აპები"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"აპების სია"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"მთავარი"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"წაშლა"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"დეინსტალაცია"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"აპის შესახებ"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"ამოშლა"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"დეინსტალაცია"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"აპის შესახებ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"მალსახმობების დაყენება"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"აპისთვის მალსახმობების დამოუკიდებლად დამატების უფლების მიცემა."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"მთავარი ეკრანის პარამეტრებისა და მალსახმობების წაკითხვა"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"დაყენება"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ეს სისტემური აპია და მისი წაშლა შეუძლებელია."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"უსახელო საქაღალდე"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> გაითიშა"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"გვერდი %1$d %2$d-დან"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"მთავარი ეკრანი %1$d, %2$d-დან"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"მთავარი ეკრანის ახალი გვერდი"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"მოგესალმებით"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"თქვენი აპის ხატულების კოპირება"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"გსურთ, ძველი მთავარი ეკრანიდან ხატულების და საქაღ. იმპორტი?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"ხატულების კოპირება"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"სტანდარტული განლაგება"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"ფონები, ვიჯეტები, &amp; პარამეტრები"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"მოსარგებად შეეხეთ &amp; დააყოვნეთ ფონზე"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"გასაგებია"</string>
     <string name="folder_opened" msgid="94695026776264709">"საქაღალდე გახსნილია, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"შეეხეთ საქაღალდის დასახურად"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"შეეხეთ ახალი სახელის შესანახად"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"შეეხეთ საქაღალდის დასახურად"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"შეეხეთ გადარქმეული სახელის შესანახად"</string>
     <string name="folder_closed" msgid="4100806530910930934">"საქაღალდე დაიხურა"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"საქაღალდეს შეეცვალა სახელი „<xliff:g id="NAME">%1$s</xliff:g>“-ად"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"საქაღალდე: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ფონები"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"პარამეტრები"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"გათიშულია თქვენი ადმინისტრატორის მიერ"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"როტაციის დაშვება"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"მიმოხილვა"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"მთავარი ეკრანის შეტრიალების დაშვება"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"ტელეფონის შეტრიალებისას"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ბრუნვა დაუშვებელია ჩვენების მიმდინარე პარამეტრებით"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"უცნობი"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ამოშლა"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ძიება"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"სიგანის შემცირება"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"სიმაღლის შემცირება"</string>
     <string name="widget_resized" msgid="9130327887929620">"ვიჯეტის ზომები შეიცვალა: სიგანე <xliff:g id="NUMBER_0">%1$s</xliff:g> სიმაღლე <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"მალსახმობები"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g>-ს აქვს <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> მალსახმობი"</string>
 </resources>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index 4b41ac9..009fa4a 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"Таңбаша қолжетімді емес"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Виджетті таңдау үшін түртіп, мықтап ұстаңыз."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Виджетті таңдау немесе арнаулы әрекеттерді таңдау үшін екі рет түртіп, ұстап тұрыңыз."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Қолданбаларды іздеу…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ені: %1$d, биіктігі: %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Қолданбаларды іздеу"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Қолданбалар жүктелуде…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"«<xliff:g id="QUERY">%1$s</xliff:g>» сұрауына сәйкес келетін қолданбалар жоқ"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> сұрауына өту"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Қосымша қолданбалар іздеу"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Бұл Негізгі экранда орын қалмады."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Қалаулылар науасында орын қалмады"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Қолданбалар"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Қолданбалар тізімі"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Негізгі"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Алып тастау"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Алмау"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Қолданба ақпары"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Жою"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Жою"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Қолданба ақпараты"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"төте пернелерді орнату"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Қолданбаға пайдаланушының қатысуынсыз төте пернелерді қосу мүмкіндігін береді."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"Негізгі экрандағы параметрлер мен төте пернелерді оқу"</string>
@@ -51,28 +53,24 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Орнату"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Бұл жүйе қолданбасы, сондықтан оны алу мүмкін емес."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Атауы жоқ қалта"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өшірілді"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d бет, барлығы %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d негізгі экран, барлығы %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Жаңа негізгі экран беті"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Қош келдіңіз"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Қолданба таңбаларын көшіру"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Бұрынғы негізгі экрандарыңыздағы таңбалар мен қалталар импортталсын ба?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"ТАҢБАЛАРДЫ КӨШІРУ"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ЖАҢАДАН БАСТАУ"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Тұсқағаздар, виджеттер және параметрлер"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Теңшеу үшін фонды түртіп, ұстап тұрыңыз"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ТҮСІНДІМ"</string>
     <string name="folder_opened" msgid="94695026776264709">"Қалта ашылды, <xliff:g id="WIDTH">%1$d</xliff:g> және <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Қалтаны жабу үшін түртіңіз"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Өзгертілген атауын сақтау үшін түртіңіз"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Қалтаны жабу үшін түртіңіз"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Қайта атауды сақтау үшін түртіңіз"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Қалта жабылды"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Қалта атауы <xliff:g id="NAME">%1$s</xliff:g> болып өзгертілді"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Артқы фондар"</string>
+    <string name="wallpaper_button_text" msgid="8404103075899945851">"Тұсқағаздар"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Параметрлер"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Әкімші өшірді"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Айналуға рұқсат ету"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Шолу"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Негізгі экранның бұрылуына рұқсат ету"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Телефон бұрылғанда"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Экранның ағымдағы параметрі айналуға рұқсат бермейді"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Белгісіз"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Алып тастау"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Іздеу"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Енін азайту"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Биіктігін азайту"</string>
     <string name="widget_resized" msgid="9130327887929620">"Виджет өлшемінің ені <xliff:g id="NUMBER_0">%1$s</xliff:g>, биіктігі <xliff:g id="NUMBER_1">%2$s</xliff:g> болып өзгертілді"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Таңбашалар"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> қолданбасына арналған <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> таңбаша"</string>
 </resources>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index f49135b..864c979 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"ផ្លូវកាត់មិនអាចប្រើបានទេ"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"ប៉ះ &amp; សង្កត់ ដើម្បី​ជ្រើស​ធាតុ​ក្រាហ្វិក។"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ប៉ះពីរដង ហើយចុចឲ្យជាប់ដើម្បីជ្រើសយកធាតុក្រាហ្វិក ឬប្រើសកម្មភាពផ្ទាល់ខ្លួន។"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ស្វែងរកកម្មវិធី…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"ទទឺង %1$d គុណនឹងកម្ពស់ %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ស្វែងរកកម្មវិធី"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"កំពុងដំណើរការកម្មវិធី..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"គ្មានកម្មវិធីដែលត្រូវជាមួយ \"<xliff:g id="QUERY">%1$s</xliff:g>\" ទេ"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"ចូលទៅកាន់ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"ស្វែងរកកម្មវិធីច្រើនទៀត"</string>
     <string name="out_of_space" msgid="4691004494942118364">"គ្មាន​បន្ទប់​នៅ​លើ​អេក្រង់​ដើម​នេះ​ទៀត​ទេ។"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"គ្មាន​បន្ទប់​​ក្នុង​ថាស​និយម​ប្រើ"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"កម្មវិធី"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"បញ្ជីកម្មវិធី"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"ដើម"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"លុប​ចេញ"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"លុប"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"ព័ត៌មាន​កម្មវិធី"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"យកចេញ"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"លុបការដំឡើង"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"ព័ត៌មាន​កម្មវិធី"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ដំឡើង​ផ្លូវកាត់"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"អនុញ្ញាត​ឲ្យ​កម្មវិធី​បន្ថែម​ផ្លូវកាត់​ ដោយ​មិន​ចាំបាច់​​អំពើ​ពី​អ្នក​ប្រើ។"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"អាន​ការ​កំណត់​ និង​ផ្លូវកាត់​​អេក្រង់​ដើម"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"រៀបចំ"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"នេះ​​​ជា​កម្មវិធី​ប្រព័ន្ធ មិន​អាច​លុប​បាន​ទេ។"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"ថត​គ្មាន​ឈ្មោះ"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"បានបិទដំណើរការ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"ទំព័រ %1$d នៃ %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"អេក្រង់​ដើម %1$d នៃ %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"ទំព័រអេក្រង់ដើមថ្មី"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"សូម​ស្វាគមន៍​"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"ចម្លង​រូបតំណាង​កម្មវិធី​របស់​អ្នក"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"នាំចូល​រូបតំណាង និង​ថត​ពី​អេក្រង់​ដើម​ចាស់​របស់​អ្នក?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"រូប​តំណាង​ច្បាប់​ចម្លង"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ចាប់ផ្ដើម​ធ្វើ​ឲ្យ​ស្រស់"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"ផ្ទាំងរូបភាព,ធាតុក្រាហ្វិក &amp; ការកំណត់"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"ប៉ះ &amp; សង្កត់​ផ្ទៃ​ខាង​ក្រោយ​ដើម្បី​ប្ដូរ​តាម​​តម្រូវ​ការ"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"យល់​ហើយ"</string>
     <string name="folder_opened" msgid="94695026776264709">"បាន​បើក​ថត <xliff:g id="WIDTH">%1$d</xliff:g> ដោយ <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"ប៉ះ ដើម្បី​បិទ​ថត"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"ប៉ះ ដើម្បី​រក្សាទុក​ការ​ប្ដូរ​ឈ្មោះ"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"ប៉ះ ដើម្បីបិទថត"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"ប៉ះដើម្បីរក្សាទុកឈ្មោះដែលបានប្តូរ"</string>
     <string name="folder_closed" msgid="4100806530910930934">"បាន​បិទ​ថត"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"បាន​ប្ដូរ​ឈ្មោះ​ថត​ជា <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ផ្ទាំង​រូបភាព"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"ការកំណត់"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"បានបិទដំណើរការដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"អនុញ្ញាតឲ្យបង្វិល"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"សង្ខេប"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"អនុញ្ញាតការបងិ្វលអេក្រង់ដើម"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"នៅពេលដែលបង្វិលទូរស័ព្ទរបស់អ្នក"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ការកំណត់អេក្រង់បច្ចុប្បន្នមិនអនុញ្ញាតការបង្វិលទេ"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"មិន​ស្គាល់"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"លុបចេញ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ស្វែងរក"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"បន្ថយទទឹង"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"បន្ថយកម្ពស់"</string>
     <string name="widget_resized" msgid="9130327887929620">"ធាតុក្រាហ្វិកដែលបានប្តូរទំហំទៅទទឹងប្រវែង <xliff:g id="NUMBER_0">%1$s</xliff:g> កម្ពស់ប្រវែង <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"ផ្លូវកាត់"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ផ្លូវកាត់សម្រាប់ <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index 48729d4..2c50567 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"ಶಾರ್ಟ್‌ಕಟ್ ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"ವಿಜೆಟ್ ಅನ್ನು ಆರಿಸಿಕೊಳ್ಳಲು ಸ್ಪರ್ಶಿಸಿ &amp; ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ಡಬಲ್ ಟ್ಯಾಪ್ ಮಾಡಿ ಮತ್ತು ವಿಜೆಟ್ ಆರಿಸಿಕೊಳ್ಳಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಅಥವಾ ಕಸ್ಟಮ್ ಕ್ರಿಯೆಗಳನ್ನು ಬಳಸಿ"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ಅಪ್ಲಿಕೇಷನ್‌ಗಳನ್ನು ಹುಡುಕಿ..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ಅಗಲ ಮತ್ತು %2$d ಎತ್ತರ"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ಅಪ್ಲಿಕೇಷನ್‌ಗಳನ್ನು ಹುಡುಕಿ"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> ಗೆ ಹೋಗಿ"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"ಮತ್ತಷ್ಟು ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಹುಡುಕಿ"</string>
     <string name="out_of_space" msgid="4691004494942118364">"ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಹೆಚ್ಚು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ಮೆಚ್ಚಿನವುಗಳ ಟ್ರೇನಲ್ಲಿ ಹೆಚ್ಚಿನ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳ ಪಟ್ಟಿ"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"ಮುಖಪುಟ"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"ತೆಗೆದುಹಾಕು"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"ಅಸ್ಥಾಪಿಸು"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"ತೆಗೆದುಹಾಕಿ"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ಅಸ್ಥಾಪಿಸು"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"ಮುಖಪುಟದ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಓದಿ"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"ಸೆಟಪ್"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ಇದೊಂದು ಅಪ್ಲಿಕೇಶನ್ ಆಗಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"ಹೆಸರಿಲ್ಲದ ಫೋಲ್ಡರ್"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d ರಲ್ಲಿ %1$d ಪುಟ"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d ರಲ್ಲಿ %1$d ಮುಖಪುಟದ ಪರದೆ"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"ಹೊಸ ಮುಖಪುಟ ಪರದೆ"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"ಸುಸ್ವಾಗತ"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"ಅಪ್ಲಿಕೇಶನ್‌ ಐಕಾನ್‌ ನಕಲಿಸು"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"ನಿಮ್ಮ ಹಳೆಯ ಮುಖಪುಟದ ಪರದೆಗಳಿಂದ ಐಕಾನ್‌ಗಳು ಮತ್ತು ಫೋಲ್ಡರ್‌ಗಳನ್ನು ಆಮದು ಮಾಡಿಕೊಳ್ಳುವುದೇ?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"ಐಕಾನ್‌ಗಳನ್ನು ನಕಲಿಸು"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ಹೊಸದಾಗಿ ಪ್ರಾರಂಭಿಸು"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"ವಾಲ್‌ಪೇಪರ್‌ಗಳು, ವಿಜೆಟ್‌ಗಳು, &amp; ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"ಕಸ್ಟಮೈಸ್ ಮಾಡಲು ಹಿನ್ನೆಲೆಯನ್ನು ಸ್ಪರ್ಶಿಸಿ &amp; ಒತ್ತಿ ಹಿಡಿಯಿರಿ"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ಅರ್ಥವಾಯಿತು"</string>
     <string name="folder_opened" msgid="94695026776264709">"ಫೋಲ್ಡರ್ ತೆರೆಯಲಾಗಿದೆ, <xliff:g id="WIDTH">%1$d</xliff:g> ಬೈ <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"ಫೋಲ್ಡರ್‌ ಮುಚ್ಚಲು ಸ್ಪರ್ಶಿಸಿ"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"ಮರುಹೆಸರನ್ನು ಉಳಿಸಲು ಸ್ಪರ್ಶಿಸಿ"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"ಫೋಲ್ಡರ್‌ ಮುಚ್ಚಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"ಮರುಹೆಸರನ್ನು ಉಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
     <string name="folder_closed" msgid="4100806530910930934">"ಫೋಲ್ಡರ್ ಮುಚ್ಚಿದೆ"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"ಫೋಲ್ಡರ್‌ ಅನ್ನು <xliff:g id="NAME">%1$s</xliff:g> ಗೆ ಮರುಹೆಸರಿಸಲಾಗಿದೆ"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,10 +67,13 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ವಾಲ್‌ಪೇಪರ್‌ಗಳು"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"ತಿರುಗಿಸುವಿಕೆಯನ್ನು ಅನುಮತಿಸಿ"</string>
-    <string name="package_state_unknown" msgid="7592128424511031410">"ಅಜ್ಞಾತ"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"ಅವಲೋಕನ"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"ಮುಖಪುಟ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸಿ"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"ಫೋನ್‌ ತಿರುಗಿಸಿದಾಗ"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ಪ್ರಸ್ತುತ ಪ್ರದರ್ಶನ ಸೆಟ್ಟಿಂಗ್ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string>
+    <string name="package_state_unknown" msgid="7592128424511031410">"ಅಪರಿಚಿತ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ತೆಗೆದುಹಾಕಿ"</string>
-    <string name="abandoned_search" msgid="891119232568284442">"ಹುಡುಕು"</string>
+    <string name="abandoned_search" msgid="891119232568284442">"ಹುಡುಕಿ"</string>
     <string name="abandoned_promises_title" msgid="7096178467971716750">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ"</string>
     <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ಈ ಐಕಾನ್ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ. ನೀವು ಅದನ್ನು ತೆಗೆದುಹಾಕಬಹುದು ಅಥವಾ ಅಪ್ಲಿಕೇಶನ್ ಹುಡುಕಬಹುದು ಮತ್ತು ಹಸ್ತಚಾಲಿತವಾಗಿ ಅದನ್ನು ಸ್ಥಾಪಿಸಬಹುದು."</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ಡೌನ್‌ಲೋಡ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ, <xliff:g id="PROGRESS">%2$s</xliff:g> ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"ಅಗಲವನ್ನು ಕಡಿಮೆ ಮಾಡಿ"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"ಎತ್ತರವನ್ನು ಕಡಿಮೆ ಮಾಡಿ"</string>
     <string name="widget_resized" msgid="9130327887929620">"ವಿಜೆಟ್ ಅನ್ನು <xliff:g id="NUMBER_0">%1$s</xliff:g> ಅಗಲ <xliff:g id="NUMBER_1">%2$s</xliff:g> ಎತ್ತರಕ್ಕೆ ಮರುಗಾತ್ರಗೊಳಿಸಲಾಗಿದೆ"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> ಗೆ <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index aa5719f..1ef438f 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"바로가기를 사용할 수 없음"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"위젯을 선택하려면 길게 터치하세요."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"위젯을 선택하려면 두 번 탭한 다음 길게 터치하거나 맞춤 액션을 사용합니다."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"앱 검색..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"너비 %1$d, 높이 %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"앱 검색"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"앱 로드 중..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\'<xliff:g id="QUERY">%1$s</xliff:g>\'와(과) 일치하는 앱이 없습니다."</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>(으)로 이동"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"더 많은 앱 검색"</string>
     <string name="out_of_space" msgid="4691004494942118364">"홈 화면에 더 이상 공간이 없습니다."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"즐겨찾기 트레이에 더 이상 공간이 없습니다."</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"앱"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"앱 목록"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"홈"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"삭제"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"제거"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"앱 정보"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"삭제"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"제거"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"앱 정보"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"바로가기 설치"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"앱이 사용자의 작업 없이 바로가기를 추가할 수 있도록 합니다."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"홈 설정 및 바로가기 읽기"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"설정"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"시스템 앱은 제거할 수 없습니다."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"이름이 없는 폴더"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> 사용 안함"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"페이지 %1$d/%2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"홈 화면 %1$d/%2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"새로운 메인 스크린 페이지"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"환영합니다."</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"앱 아이콘 복사"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"이전 메인 스크린에서 아이콘과 폴더를 가져오시겠습니까?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"아이콘 복사"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"새로 시작"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"배경화면, 위젯, 설정"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"백그라운드를 길게 터치하여 맞춤설정합니다."</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"확인"</string>
     <string name="folder_opened" msgid="94695026776264709">"폴더 열림(<xliff:g id="WIDTH">%1$d</xliff:g>X<xliff:g id="HEIGHT">%2$d</xliff:g>)"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"터치하여 폴더를 닫음"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"터치하여 바꾼 이름을 저장"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"탭하여 폴더 닫기"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"탭하여 변경된 이름 저장"</string>
     <string name="folder_closed" msgid="4100806530910930934">"폴더 닫음"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"폴더 이름 변경: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"폴더: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"배경화면"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"설정"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"관리자가 사용 중지함"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"회전 허용"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"개요"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"메인 스크린 회전 허용"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"휴대전화 회전 시"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"현재 표시 설정에는 회전 기능이 허용되지 않습니다."</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"알 수 없음"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"삭제"</string>
     <string name="abandoned_search" msgid="891119232568284442">"검색"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"폭 줄이기"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"높이 줄이기"</string>
     <string name="widget_resized" msgid="9130327887929620">"폭 <xliff:g id="NUMBER_0">%1$s</xliff:g>, 높이 <xliff:g id="NUMBER_1">%2$s</xliff:g>로 위젯 크기 조정됨"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"바로가기"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g>에 사용 가능한 단축키 <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>개"</string>
 </resources>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index 5d4c8a3..71e02d0 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"Кыска жол жок"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Виджетти тандаш үчүн, басып туруңуз"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Виджет тандоо үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Колдонмолорду издөө…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Туурасы: %1$d, бийиктиги: %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Колдонмолорду издөө"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Колдонмолор жүктөлүүдө…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" дал келген колдонмолор табылган жок"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> сурамына өтүңүз"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Көбүрөөк колдонмолорду издөө"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Бул Үй экранында бош орун жок."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Тандамалдар тайпасында орун калган жок"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Колдонмолор"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Колдонмолор тизмеси"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Үйгө"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Алып салуу"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Чечип салуу"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Колдонмо тууралуу"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Алып салуу"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Чыгарып салуу"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Колдонмо тууралуу"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"тез чакырмаларды орнотуу"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Колдонмого колдонуучуга кайрылбастан тез чакырма кошууга уруксат берет."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"Үйдүн тууралоолорун жана тез чакырмаларын окуу"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Орнотуу"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Бул системдик колдонмо жана аны чечкенге болбойт."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Аты жок фолдер"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өчүрүлгөн"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d ичинен %1$d барак"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Үй экраны %2$d ичинен %1$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Жаңы башкы экран барагы"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Кош келиңиз"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Колдонмоңуздун сүрөтчөлөрүн көчүрүү"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Эски үй экрандарыңыздан сүрөтчөлөр жана фолдерлер импорттолсунбу?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"СҮРӨТЧӨЛӨРДҮ КӨЧҮРҮҮ"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ТАЗАСЫН БАШТОО"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Тушкагаздар, виджеттер &amp; жөндөөлөр"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Өзгөчөлөштүрүү үчүн фонго тийип &amp; коё бербей туруңуз"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ТҮШҮНДҮМ"</string>
     <string name="folder_opened" msgid="94695026776264709">"Фолдер ачылды, туурасы <xliff:g id="WIDTH">%1$d</xliff:g>, бийиктиги <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Фолдерди жабыш үчүн тийиңиз"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Тийип, аттын өзгөртүлүшүн сактаңыз"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Куржунду жабуу үчүн таптаңыз"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Өзгөртүлгөн аталышын сактоо үчүн таптаңыз"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Фолдер жабык"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Фолдердин аты <xliff:g id="NAME">%1$s</xliff:g> деп өзгөртүлдү"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Тууралоолор"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администраторуңуз өчүрүп койгон"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Айлантууга уруксат берүү"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Көз жүгүртүү"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Башкы экранды айлантууга уруксат берүү"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Телефон айланганда"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Экранды айлантуу параметри өчүрүлгөн"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Белгисиз"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Алып салуу"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Издөө"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Ичкертүү"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Жапыздатуу"</string>
     <string name="widget_resized" msgid="9130327887929620">"Виджеттин кеңдиги <xliff:g id="NUMBER_0">%1$s</xliff:g> бийиктиги <xliff:g id="NUMBER_1">%2$s</xliff:g> болду"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Кыска жолдор"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> колдонмосуна <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> кыска жол бар"</string>
 </resources>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index fa36231..3915759 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -15,9 +15,6 @@
 -->
 
 <resources>
-<!-- QSB -->
-    <dimen name="toolbar_button_vertical_padding">8dip</dimen>
-    <dimen name="toolbar_button_horizontal_padding">0dip</dimen>
-     <!-- Container -->
+<!-- Container -->
      <item name="container_margin" format="fraction" type="fraction">12%</item>
 </resources>
diff --git a/res/values-land/styles.xml b/res/values-land/styles.xml
deleted file mode 100644
index c5a76d5..0000000
--- a/res/values-land/styles.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2008 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.
-*/
--->
-
-<resources>
-
-    <!-- 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>
-
-    <!-- This style applies to the drop target when it is shown in the sidebar -->
-    <style name="DropTargetButton" parent="DropTargetButtonBase">
-        <item name="android:layout_height">wrap_content</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>
-    </style>
-
-</resources>
\ No newline at end of file
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index e93a7e6..e231102 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"ແອັບຯ​ໃຊ້​ບໍ່​ໄດ້"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ແອັບຯ​ທີ່​ດາວ​ໂຫລດ​ແລ້ວ​ຖືກ​ປິດ​ການ​ນຳ​ໃຊ້​ໃນ Safe mode"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"​ວິດ​ເຈັດ​ຖືກ​ປິດ​ໃນ Safe mode"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"ບໍ່ສາມາດໃຊ້ທາງລັດໄດ້"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"ສຳພັດຄ້າງໄວ້ ເພື່ອຈັບວິດເຈັດ."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ແຕະ​ຄ້າງ​ໄວ້ ເພື່ອ​ເລືອກວິດ​ເຈັດ ຫຼື ໃຊ້​ການ​ດຳ​ເນີນ​ການ​ກຳ​ນົດ​ເອງ."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ຄົ້ນຫາແອັບ"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"ກວ້າງ %1$d ຄູນສູງ %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ຊອກຫາແອັບ"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"​ກຳ​ລັງ​ໂຫລດ​ແອັບ..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"ບໍ່​ພົບ​ແອັບ​ໃດ​ທີ່​ກົງ​ກັນ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"ໄປ​ທີ່ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"ຊອກຫາແອັບເພີ່ມເຕີມ"</string>
     <string name="out_of_space" msgid="4691004494942118364">"ບໍ່ມີຫ້ອງເຫຼືອໃນໜ້າຈໍຫຼັກນີ້."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ບໍ່ມີບ່ອນຫວ່າງໃນຖາດສຳລັບເກັບສິ່ງທີ່ໃຊ້ເປັນປະຈຳ"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"ແອັບຯ"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"ລາຍຊື່ແອັບ"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"ໜ້າຫຼັກ"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"ລຶບ"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"ຖອນ​ການ​ຕິດ​ຕັ້ງ"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"ຂໍ້ມູນແອັບຯ"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"ເອົາ​ອອກ"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ຖອນ​ການ​ຕິດ​ຕັ້ງ"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"ຂໍ້ມູນແອັບ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ຕິດຕັ້ງທາງລັດ"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ອະນຸຍາດໃຫ້ແອັບຯ ເພີ່ມທາງລັດໂດຍບໍ່ຕ້ອງຮັບການຢືນຢັນຈາກຜູ່ໃຊ້."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"ອ່ານການຕັ້ງຄ່າໜ້າຫຼັກ ແລະທາງລັດ"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"ຕິດຕັ້ງ"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ນີ້ແມ່ນແອັບຯຂອງລະບົບ ແລະບໍ່ສາມາດຖອນການຕິດຕັ້ງອອກໄດ້."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"ໂຟນເດີຍັງບໍ່ຖືກຕັ້ງຊື່"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"ປິດການນຳໃຊ້ <xliff:g id="APP_NAME">%1$s</xliff:g> ແລ້ວ"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"ໜ້າ %1$d ຈາກ %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"ໜ້າຈໍຫຼັກ %1$d ໃນ %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"ໜ້າ​ຂອງ​ໜ້າ​ຈໍ​ຫຼັກ​ໃໝ່"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"ຍິນດີຕ້ອນຮັບ"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"ສຳເນົາໄອຄອນແອັບຯຂອງທ່ານ"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"ນຳເຂົ້າໄອຄອນ ແລະ ໂຟນເດີຈາກໂຮມສະກຣີນອັນເກົ່າຂອງທ່ານບໍ່?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"ສຳເນົາໄອຄອນ"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ເລີ່ມຕົ້ນໃໝ່"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"​ຮູບ​ພື້ນຫຼັງ, ວິດເຈັດ, &amp; ​ການ​ຕັ້ງ​ຄ່າ"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"ແຕະທີ່​ພາບ​ພື້ນ​ຫລັງ​ຄ້າງ​ໄວ້​ເພື່ອ​ປັບ​ແຕ່ງ"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ເຂົ້າໃຈແລ້ວ"</string>
     <string name="folder_opened" msgid="94695026776264709">"ເປີດໂຟນເດີແລ້ວ, <xliff:g id="WIDTH">%1$d</xliff:g> ຄູນ <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"ສຳພັດເພື່ອປິດໂຟນເດີ"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"ສຳພັດເພື່ອບັນທຶກການປ່ຽນຊື່"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"ແຕະເພື່ອປິດໂຟນເດີ"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"ແຕະເພື່ອບັນທຶກການປ່ຽນຊື່"</string>
     <string name="folder_closed" msgid="4100806530910930934">"ປິດໂຟນເດີແລ້ວ"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"ປ່ຽນຊື່ໂຟນເດີເປັນ <xliff:g id="NAME">%1$s</xliff:g> ແລ້ວ"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ພາບພື້ນຫຼັງ"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"ການຕັ້ງຄ່າ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ຖືກປິດການນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"ອະ​ນຸ​ຍາດ​ການ​ໝຸນ"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"ພາບຮວມ"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"ອະນຸຍາດໃຫ້ໝຸນໜ້າຈໍທຳອິດໄດ້"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"ເມື່ອໝຸນໂທລະສັບ"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ການຕັ້ງຄ່າສະແດງຜົນປັດຈຸບັນບໍ່ອະນຸຍາດໃຫ້ໝຸນໄດ້"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"​ບໍ່​ຮູ້​ຈັກ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ລຶບ​"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ຊອກຫາ"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"ຫຼຸດ​ລວງ​ກ້​ວາງ​ລົງ"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"ຫຼຸດ​ລວງ​ສູງ​ລົງ"</string>
     <string name="widget_resized" msgid="9130327887929620">"ປ່ຽນ​ຂະ​ໜາດ​ວິດ​ເຈັດ​ເປັນ​ລວງ​ກ້​ວາງ <xliff:g id="NUMBER_0">%1$s</xliff:g> ລວງ​ສູງ <xliff:g id="NUMBER_1">%2$s</xliff:g> ແລ້ວ"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"ທາງລັດ"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ທາງລັດສຳລັບ <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index f9a1a09..13ebaa3 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Programa nepasiekiama"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Atsisiųsta programa išjungta Saugos režimu"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Valdikliai išjungti Saugiame režime"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Sparčiojo klavišo negalima naudoti"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Palieskite ir laikykite, kad pasirinkt. valdiklį."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dukart palieskite ir laikykite, kad pasirinktumėte valdiklį ar naudotumėte tinkintus veiksmus."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Ieškoti programų..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d plotis ir %2$d aukštis"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Ieškoti programų"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Įkeliamos programos..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nerasta jokių užklausą „<xliff:g id="QUERY">%1$s</xliff:g>“ atitinkančių programų"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Eiti į <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Ieškoti daugiau programų"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Šiame pagrindiniame ekrane vietos nebėra."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Mėgstamiausių dėkle nebėra vietos"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Programos"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Programų sąrašas"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Pagrindinis"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Ištrinti"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Pašalinti"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Programos informacija"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Ištrinti"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Pašalinti"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Programos inform."</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"įdiegti sparčiuosius klavišus"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Programai leidžiama pridėti sparčiuosius klavišus be naudotojo įsikišimo."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"skaityti pagrindinio puslapio nustatymus ir sparčiuosius klavišus"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Sąranka"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Tai sistemos programa ir jos negalima pašalinti."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Aplankas be pavadinimo"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ išjungta"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d psl. iš %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d pagrindinis ekranas iš %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Naujas pagrindinio ekrano puslapis"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Sveiki"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Programų piktogramų kopij."</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Importuoti piktogramas ir aplankus iš senų pagr. ekranų?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPIJUOTI PIKTOGRAMAS"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"PRADĖTI IŠ NAUJO"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Ekrano fonai, valdikliai ir nustatymai"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Jei norite tinkinti, palieskite ir palaikykite foną"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"SUPRATAU"</string>
     <string name="folder_opened" msgid="94695026776264709">"Atidarytas aplankas, <xliff:g id="WIDTH">%1$d</xliff:g> ir <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Palieskite, kad uždarytumėte aplanką"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Palieskite, kad išsaugotumėte naują pavadinimą"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Palieskite, kad uždarytumėte aplanką"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Palieskite, kad išsaugotumėte pakeistą pavadinimą"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Aplankas uždarytas"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Aplankas pervardytas kaip „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Aplankas: „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ekrano fonai"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Nustatymai"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Išjungė administratorius"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Leisti kaitaliojimą"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Apžvalga"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Leisti pasukti pagrindinį ekraną"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Kai telefonas pasukamas"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Naudojant dabartinį pateikties nustatymą neleidžiama pasukti"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Nežinoma"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Pašalinti"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Ieškoti"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Sumažinti plotį"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Sumažinti aukštį"</string>
     <string name="widget_resized" msgid="9130327887929620">"Valdiklio dydis pakeistas: plotis – <xliff:g id="NUMBER_0">%1$s</xliff:g>, aukštis – <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Spartieji klavišai"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"Programos „<xliff:g id="APP_NAME">%2$s</xliff:g>“ spartieji klavišai (<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>)"</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index d9b59ad..924b399 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Lietotne nav pieejama."</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Lejupielādētā lietotne ir atspējota drošajā režīmā."</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Logrīki atspējoti drošajā režīmā"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Saīsne nav pieejama."</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Lai izvēlētos logrīku, pieskarieties un turiet to."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Lai atlasītu logrīku, veiciet dubultskārienu uz tā un turiet to vai arī veiciet pielāgotas darbības."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Meklēt lietotnes…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d plats un %2$d augsts"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Meklēt lietotnes"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Notiek lietotņu ielāde…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Vaicājumam “<xliff:g id="QUERY">%1$s</xliff:g>” neatbilda neviena lietotne."</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Doties uz: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Meklēt citas lietotnes"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Šajā sākuma ekrānā vairs nav vietas."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Izlases joslā vairs nav vietas."</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Lietotnes"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Lietotņu saraksts"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Sākums"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Noņemt"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Atinstalēt"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Lietotnes informācija"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Noņemt"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Atinstalēt"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Lietotnes informācija"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalēt saīsnes"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ļauj lietotnei pievienot saīsnes, nejautājot lietotājam."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"lasīt sākuma ekrāna iestatījumus un saīsnes"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Notiek iestatīšana"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Šī ir sistēmas lietotne, un to nevar atinstalēt."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Mape bez nosaukuma"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> ir atspējota"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d. lapa no %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Sākuma ekrāns: %1$d no %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Jauna sākuma ekrāna lapa"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Laipni lūdzam!"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Lietotņu ikonu kopēšana"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Vai importēt ikonas, mapes no iepriekšējiem sākuma ekrāniem?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPĒT IKONAS"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"SĀKT NO SĀKUMA"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Fona tapetes, logrīki un iestatījumi"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Lai pielāgotu, pieskarieties fonam un turiet to nospiestu."</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"SAPRATU!"</string>
     <string name="folder_opened" msgid="94695026776264709">"Atvērta mape: <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Pieskarieties, lai aizvērtu mapi."</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Pieskarieties, lai saglabātu pārdēvēto nosaukumu."</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Pieskarieties, lai aizvērtu mapi."</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Pieskarieties, lai saglabātu jauno nosaukumu."</string>
     <string name="folder_closed" msgid="4100806530910930934">"Mape aizvērta"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Mape pārdēvēta par: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Mape: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fona tapetes"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Iestatījumi"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Atspējojis administrators"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Rotācijas atļauja"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Kopsavilkums"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Atļaut sākuma ekrāna pagriešanu"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Pagriežot tālruni"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Pašreizējā displeja iestatījumā nav atļauta pagriešana."</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Nezināma"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Noņemt"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Meklēt"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Samazināt platumu"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Samazināt augstumu"</string>
     <string name="widget_resized" msgid="9130327887929620">"Logrīka lielums mainīts — platums: <xliff:g id="NUMBER_0">%1$s</xliff:g>, augstums: <xliff:g id="NUMBER_1">%2$s</xliff:g>."</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Saīsnes"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> saīsnes lietotnei <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index a1bf843..7e44707 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -26,53 +26,51 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"Кратенката не е достапна"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Допри и задржи за да се избере виџетот."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Допрете двапати и задржете за да изберете додаток или да користите приспособени дејства."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Пребарувај апликации…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d широк на %2$d висок"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Пребарување апликации"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Се вчитуваат апликации…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Не се најдени апликации што одговараат на „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Оди на <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Пребарај други апликации"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Нема повеќе простор на овој екран на почетната страница."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Нема повеќе простор на лентата „Омилени“"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Апликации"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Список со апликации"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Почетна страница"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Отстрани"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Деинсталирај"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Информации за апликацијата"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Отстрани"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталирај"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Инф. за апликација"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирај кратенки"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Овозможува апликацијата да додава кратенки без интервенција на корисникот."</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"прочитај подесувања и кратенки на почетна страница"</string>
+    <string name="permlab_read_settings" msgid="1941457408239617576">"чита поставки и кратенки на почетна страница"</string>
     <string name="permdesc_read_settings" msgid="5833423719057558387">"Овозможува апликацијата да ги менува подесувањата и кратенките на почетната страница."</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"напиши подесувања и кратенки на почетна страница"</string>
+    <string name="permlab_write_settings" msgid="3574213698004620587">"пишува поставки и кратенки на почетна страница"</string>
     <string name="permdesc_write_settings" msgid="5440712911516509985">"Овозможува апликацијата да ги менува подесувањата и кратенките на почетната страница."</string>
     <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> нема дозвола за телефонски повици"</string>
     <string name="gadget_error_text" msgid="6081085226050792095">"Проблем при вчитувањето на виџетот"</string>
     <string name="gadget_setup_text" msgid="8274003207686040488">"Поставување"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ова е системска апликација и не може да се деинсталира."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Неименувана папка"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> е оневозможена"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Страница %1$d од %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Екран на почетна страница %1$d од %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Нова страница на почетен екран"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Добредојдовте"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Копирај икони за апликација"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Зачувај икони и папки од твоите стари почетни страни?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"КОПИРАЈ ИКОНИ"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"СТАРТУВАЈ ОД ПОЧЕТОК"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Тапети, додатоци и поставки"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Допрете и задржете на заднината за да приспособите"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"СФАТИВ"</string>
     <string name="folder_opened" msgid="94695026776264709">"Отворена е папка, <xliff:g id="WIDTH">%1$d</xliff:g> на <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Допри за да се затвори папката"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Допри за да се зачува преименувањето"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Допрете за да ја затворите папката"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Допрете за да го зачувате преименувањето"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Папката е затворена"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Папката е преименувана во <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
+    <string name="widget_button_text" msgid="2880537293434387943">"Додатоци"</string>
+    <string name="wallpaper_button_text" msgid="8404103075899945851">"Позадини"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Поставки"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Оневозможено од администраторот"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Дозволи ротација"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Краток преглед"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволете ротација на Почетниот екран"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Кога телефонот се ротира"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Тековната поставка на Екранот не дозволува ротација"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Отстрани"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Барај"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Намали ширина"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Намали висина"</string>
     <string name="widget_resized" msgid="9130327887929620">"Големината на виџетот е променета на ширина <xliff:g id="NUMBER_0">%1$s</xliff:g> висина <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Кратенки"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> кратенки за <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 09a5fc7..941d1ac 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"കുറുക്കുവഴി ലഭ്യമല്ല"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"ഒരു വിജറ്റ് ചേർക്കുന്നതിന് അത് സ്‌പർശിച്ച് പിടിക്കുക."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"വിജറ്റ് തിരഞ്ഞെടുക്കാനോ ഇഷ്ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കാനോ രണ്ടുതവണ ടാപ്പുചെയ്ത് പിടിക്കുക."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ആപ്പ്‌സ് തിരയുക…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d വീതിയും %2$d ഉയരവും"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ആപ്പ്‌സ് തിരയുക"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"ആപ്പ്‌സ് ലോഡുചെയ്യുന്നു..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പ്‌സൊന്നും കണ്ടെത്തിയില്ല"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> എന്നതിലേക്ക് പോവുക"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"കൂടുതൽ ആപ്പുകൾക്ക് തിരയുക"</string>
     <string name="out_of_space" msgid="4691004494942118364">"ഈ ഹോം സ്‌ക്രീനിൽ ഒഴിവൊന്നുമില്ല."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"പ്രിയപ്പെട്ടവയുടെ ട്രേയിൽ ഒഴിവൊന്നുമില്ല"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"അപ്ലിക്കേഷനുകൾ"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"അപ്ലിക്കേഷനുകളുടെ ലിസ്‌റ്റ്"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"ഹോം"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"നീക്കംചെയ്യുക"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"അണ്‍ഇസ്റ്റാളുചെയ്യുക"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"ആപ്പ് വിവരം"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"നീക്കംചെയ്യുക"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"അൺഇൻസ്റ്റാളുചെയ്യുക"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"ആപ്പ് വിവരം"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ ചേർക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"ഹോം ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റീഡുചെയ്യുക"</string>
@@ -51,28 +53,24 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"സജ്ജീകരിക്കുക"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ഇതൊരു സിസ്‌റ്റം അപ്ലിക്കേഷനായതിനാൽ അൺഇൻസ്‌റ്റാളുചെയ്യാനാവില്ല."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"പേരുനൽകാത്ത ഫോൾഡർ"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> പ്രവർത്തനരഹിതമാക്കി"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"പേജ് %1$d / %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"ഹോം സ്‌ക്രീൻ %1$d / %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"പുതിയ ഹോം സ്ക്രീൻ പേജ്"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"സ്വാഗതം"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"നിങ്ങളുടെ അപ്ലിക്കേഷൻ ഐക്കണുകൾ പകർത്തുക"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"നിങ്ങളുടെ പഴയ ഹോം സ്ക്രീനുകളിൽ നിന്ന് ഐക്കണുകളും ഫോൾഡറുകളും ഇമ്പോർട്ടുചെയ്യണോ?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"ഐക്കണുകൾ പകർത്തുക"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"പുതുതായി ആരംഭിക്കുക"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"വാൾപേപ്പറുകൾ, വിജറ്റുകൾ, ക്രമീകരണങ്ങൾ എന്നിവ"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"ഇഷ്‌ടാനുസൃതമാക്കുന്നതിന് പശ്‌ചാത്തലം സ്‌പർശിച്ചുപിടിക്കുക"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"മനസ്സിലായി"</string>
     <string name="folder_opened" msgid="94695026776264709">"ഫോൾഡർ തുറന്നു, <xliff:g id="WIDTH">%1$d</xliff:g> / <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"ഫോൾഡർ അടയ്ക്കാൻ സ്‌പർശിക്കുക"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"പേരുമാറ്റം സംരക്ഷിക്കുന്നതിന് സ്‌പർശിക്കുക"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"ഫോൾഡർ അടയ്ക്കുന്നതിന് ടാപ്പുചെയ്യുക"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"പേരുമാറ്റം സംരക്ഷിക്കുന്നതിന് ടാപ്പുചെയ്യുക"</string>
     <string name="folder_closed" msgid="4100806530910930934">"ഫോൾഡർ അടച്ചു"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"ഫോൾഡറിന്റെ പേര് <xliff:g id="NAME">%1$s</xliff:g> എന്നായി മാറ്റി"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"വിജറ്റുകൾ"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"വാൾപേപ്പറുകൾ"</string>
-    <string name="settings_button_text" msgid="8119458837558863227">"ക്രമീകരണങ്ങൾ"</string>
+    <string name="wallpaper_button_text" msgid="8404103075899945851">"വാൾപേപ്പർ"</string>
+    <string name="settings_button_text" msgid="8119458837558863227">"ക്രമീകരണം"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"അഡ്മിൻ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"തിരിക്കൽ അനുവദിക്കുക"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"കാഴ്ച"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"ഹോം സ്ക്രീൻ തിരിക്കൽ അനുവദിക്കുക"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"ഫോൺ തിരിച്ച നിലയിലായിരിക്കുമ്പോൾ"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"നിലവിലെ ഡിസ്പ്ലേ ക്രമീകരണം തിരിക്കൽ അനുവദിക്കുന്നില്ല"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"അജ്ഞാതം"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"നീക്കംചെയ്യുക"</string>
     <string name="abandoned_search" msgid="891119232568284442">"തിരയുക"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"വീതി കുറയ്‌ക്കുക"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"ഉയരം കുറയ്‌ക്കുക"</string>
     <string name="widget_resized" msgid="9130327887929620">"വീതി <xliff:g id="NUMBER_0">%1$s</xliff:g> ഉയരം <xliff:g id="NUMBER_1">%2$s</xliff:g>-ലേക്ക് വിഡ്‌ജെറ്റിന്റെ വലുപ്പം മാറ്റി"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"കുറുക്കുവഴികൾ"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> ആപ്പിനുള്ള <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> കുറുക്കുവഴികൾ"</string>
 </resources>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index c4bef0b..45e1856 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Апп-г ашиглах боломжгүй"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Татаж авсан апп-г Аюулгүй горим дотроос идэвхгүйжүүлсэн"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Safe горимд виджетүүдийг идэвхгүйжүүлсэн"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Товчлол алга"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Виджетийг авах бол хүрээд барина уу."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Жижиг хэрэгсэл авах болон тохируулсан үйлдлийг ашиглахын тулд 2 удаа товшоод барина уу."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Апп хайх..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d өргөн %2$d өндөр"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Апп хайх"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Аппликейшныг ачаалж байна..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"-тай нийцэх апп олдсонгүй"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> руу очих"</string>
+    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"-д нийцэх апп олдсонгүй"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Бусад апп-г хайх"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Энэ Нүүр дэлгэц зайгүй."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"\"Дуртай\" трей дээр өөр зай байхгүй байна"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Апп"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Апп-н жагсаалт"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Нүүр"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Устгах"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Устгах"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Апп мэдээлэл"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Арилгах"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Устгах"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Апп-н мэдээлэл"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"товчлол суулгах"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Апп нь хэрэглэгчийн оролцоогүйгээр товчлолыг нэмэж чадна"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"Нүүрний тохиргоо болон товчлолыг унших"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Тохируулга"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Энэ апп нь системийн апп ба устгах боломжгүй."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Нэргүй фолдер"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г идэвхгүй болгосон"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d-н %1$d хуудас"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d-н Нүүр дэлгэц %1$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Шинэ үндсэн нүүр хуудас"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Тавтай морилно уу"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Таны апп дүрсүүдийг хуулах"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Таны хуучин Үндсэн дэлгэц дээрх дүрсүүдийг импорт хийх үү?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"ДҮРСҮҮДИЙГ ХУУЛАХ"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ШИНЭЭР ЭХЛЭХ"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Дэвсгэр зураг, виджет, &amp; тохиргоо"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Тааруулахын тулд арын дэлгэцэнд хүрээд &amp; барина уу"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"Ойлголоо"</string>
     <string name="folder_opened" msgid="94695026776264709">"<xliff:g id="WIDTH">%1$d</xliff:g> <xliff:g id="HEIGHT">%2$d</xliff:g> фолдер нээгдэв"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Фолдер хаах бол хүрнэ үү"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Шинэ нэрийг хадгалах бол хүрнэ үү"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Фолдерийг хаахын тулд дарна уу"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Шинэ нэрийг хадгалахын тулд дарна уу."</string>
     <string name="folder_closed" msgid="4100806530910930934">"Фолдер хаагдав"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Фолдерын нэр <xliff:g id="NAME">%1$s</xliff:g> болов"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ханын зураг"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Тохиргоо"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Таны админ идэвхгүй болгосон"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Эргүүлэхийг зөвшөөрөх"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Тойм"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Нүүр дэлгэцийг эргүүлэхийг зөвшөөрөх"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Утсыг эргүүлсэн үед"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Дэлгэцийн одоогийн тохиргоогоор эргүүлэх боломжгүй"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Тодорхойгүй"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Устгах"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Хайх"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Нарийсгах"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Намсгах"</string>
     <string name="widget_resized" msgid="9130327887929620">"Виджэтийн өргөн <xliff:g id="NUMBER_0">%1$s</xliff:g>, өндөр <xliff:g id="NUMBER_1">%2$s</xliff:g> болсон"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Товчлол"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g>-н <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> товчлол"</string>
 </resources>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index e9e561f..1ec443c 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"शॉर्टकट उपलब्ध नाही"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"विजेट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"एक विजेट निवडण्यासाठी दोनदा टॅप करा आणि धरून ठेवा किंवा सानुकूल क्रिया वापरा."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"अॅप्स शोधा..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d रूंद बाय %2$d उंच"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"अॅप्स शोधा"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"अॅप्स लोड करीत आहे..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> वर जा"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"अधिक अॅप्स शोधा"</string>
     <string name="out_of_space" msgid="4691004494942118364">"या मुख्य स्क्रीनवर आणखी जागा नाही."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"आवडीच्या ट्रे मध्ये आणखी जागा नाही"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"अॅप्स"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"अॅप्स सूची"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"मुख्‍यपृष्‍ठ"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"काढा"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"विस्थापित करा"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"अॅप माहिती"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"काढा"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"विस्थापित करा"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"अॅप माहिती"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट स्‍थापित करा"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अॅप ला अनुमती देते."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट वाचा"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"सेटअप"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"हा सिस्टम अॅप आहे आणि विस्थापित केला जाऊ शकत नाही."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"अनामित फोल्डर"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> अक्षम केला आहे"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d पैकी %1$d पृष्ठ"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d पैकी %1$d मुख्य स्क्रीन"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"नवीन मुख्य स्क्रीन पृष्ठ"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"सुस्वागतम"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"आपली अॅप चिन्हे कॉपी करा"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"आपल्या जुन्या मुख्य स्क्रीनवरून चिन्हे आणि फोल्डर आयात करायची?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"चिन्हे कॉपी करा"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"नव्याने प्रारंभ करा"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"वॉलपेपर, विजेट आणि सेटिंग्ज"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"सानुकूल करण्यासाठी पार्श्वभूमीस स्पर्श करा आणि धरुन ठेवा"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"समजले"</string>
     <string name="folder_opened" msgid="94695026776264709">"फोल्डर उघडले, <xliff:g id="WIDTH">%1$d</xliff:g> बाय <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"फोल्डर बंद करण्यासाठी स्पर्श करा"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"नवे नाव जतन करण्यासाठी स्पर्श करा"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"फोल्डर बंद करण्यासाठी टॅप करा"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"पुनर्नामित करणे जतन करण्यासाठी टॅप करा"</string>
     <string name="folder_closed" msgid="4100806530910930934">"फोल्डर बंद"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"फोल्डरचे नाव बदलून <xliff:g id="NAME">%1$s</xliff:g> असे ठेवले"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"सेटिंग्ज"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपल्या प्रशासकाने अक्षम केले"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"फिरविण्‍यास अनुमती द्या"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"विहंगावलोकन"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"मुख्यस्क्रीन फिरविण्‍यास अनुमती द्या"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"फोन फिरविला जातो तेव्हा"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"वर्तमान प्रदर्शन सेटिंग फिरविण्यास परवानगी देत नाही"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"काढा"</string>
     <string name="abandoned_search" msgid="891119232568284442">"शोधा"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"रुंदी कमी करा"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"उंची कमी करा"</string>
     <string name="widget_resized" msgid="9130327887929620">"विजेटचा आकार रुंदी <xliff:g id="NUMBER_0">%1$s</xliff:g> उंची <xliff:g id="NUMBER_1">%2$s</xliff:g> मध्ये बदलला"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"शॉर्टकट"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> साठी <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> शॉर्टकट"</string>
 </resources>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 423cba4..215315a 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -26,20 +26,22 @@
     <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>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widget dilumpuhkan dalam mod Selamat"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Pintasan tidak tersedia"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Sentuh &amp; tahan untuk mengambil widget."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ketik dua kali &amp; tahan untuk mengambil widget atau menggunakan tindakan tersuai"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Cari Apl..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Lebar %1$d kali tinggi %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Cari Apl"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Memuatkan Apl…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Tiada Apl yang ditemui sepadan dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Pergi ke <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Cari lagi apl"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Tiada lagi ruang pada skrin Laman Utama ini."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Tiada ruang dalam dulang Kegemaran lagi"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Apl"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Senarai apl"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Laman Utama"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Alih keluar"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Nyahpasang"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Maklumat apl"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Alih keluar"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Nyahpasang"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Maklumat apl"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"pasang pintasan"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Membenarkan apl menambah pintasan tanpa campur tangan pengguna."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"baca tetapan dan pintasan Laman Utama"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Persediaan"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini ialah apl sistem dan tidak boleh dinyahpasang."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Folder Tanpa Nama"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> dilumpuhkan"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Halaman %1$d daripada %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Skrin Laman Utama %1$d daripada %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Halaman skrin utama baharu"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Selamat datang"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Salin ikon apl anda"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Import ikon dan folder dari skrin Laman Utama lama anda?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"SALIN IKON"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"MULAKAN YANG BAHARU"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Kertas dinding, widget &amp; tetapan"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Sentuh &amp; tahan latar belakang untuk memperibadikan"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"FAHAM"</string>
     <string name="folder_opened" msgid="94695026776264709">"Folder dibuka, <xliff:g id="WIDTH">%1$d</xliff:g> kali <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Sentuh untuk menutup folder"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Sentuh untuk menyimpan penamaan semula"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Ketik untuk menutup folder"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Ketik untuk menyimpan penamaan semula"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Folder ditutup"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Folder dinamakan semula kepada <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Kertas dinding"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Tetapan"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dilumpuhkan oleh pentadbir anda"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Benarkan putaran"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Ikhtisar"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Benarkan putaran Skrin Utama"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Apabila telefon diputar"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Tetapan Paparan semasa tidak membenarkan putaran"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Tidak diketahui"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Alih keluar"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Carian"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Kurangkan kelebaran"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Kurangkan ketinggian"</string>
     <string name="widget_resized" msgid="9130327887929620">"Saiz widget diubah menjadi <xliff:g id="NUMBER_0">%1$s</xliff:g> lebar <xliff:g id="NUMBER_1">%2$s</xliff:g> tinggi"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Pintasan"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> pintasan untuk <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index fade6b8..bc4e484 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -22,24 +22,26 @@
     <string name="app_name" msgid="649227358658669779">"ဖွင့်တင်စက်၃"</string>
     <string name="folder_name" msgid="7371454440695724752"></string>
     <string name="work_folder_name" msgid="3753320833950115786">"အလုပ်"</string>
-    <string name="activity_not_found" msgid="8071924732094499514">"အပ်ပလီကေးရှင်း မထည့်သွင်းထားပါ"</string>
-    <string name="activity_not_available" msgid="7456344436509528827">"App လက်လှမ်း မမှီပါ"</string>
-    <string name="safemode_shortcut_error" msgid="9160126848219158407">"ဒေါင်းလုဒ် appကို လုံခြုံရေး မုဒ်ထဲမှာ ပိတ်ထား"</string>
+    <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="shortcut_not_available" msgid="2536503539825726397">"ဖြတ်လမ်း မရနိုင်ပါ"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"ဝဒ်ဂျက်တစ်ခုကို ကောက်ယူရန် ဖိနှိပ်ထားပါ"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ဝစ်ဂျက်တစ်ခုကိုရယူရန် သို့မဟုတ် စိတ်ကြိုက်လုပ်ဆောင်မှုများကို အသုံးပြုရန် နှစ်ချက်တို့ပြီး ကိုင်ထားပါ။"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"အက်ပ်များကို ရှာဖွေပါ…"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"App များ ရယူနေစဉ်..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"အလျား %1$d နှင့် အမြင့် %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ရှာဖွေမှု အက်ပ်များ"</string>
+    <string name="all_apps_loading_message" msgid="7557140873644765180">"အက်ပ်များ ရယူနေစဉ်..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" နှင့်ကိုက်ညီသည့် အပ်ဖ်များမတွေ့ပါ"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>ကို သွားပါ"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"နောက်ထပ် အက်ပ်များကို ရှာပါ"</string>
     <string name="out_of_space" msgid="4691004494942118364">"ဤပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"အနှစ်သက်ဆုံးများ ထားရာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"အပ်ပလီကေးရှင်းများ"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"အက်ပ်စာရင်း"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"ပင်မစာမျက်နှာ"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"ဖယ်ရှာခြင်း"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"ဖယ်ရှားခြင်း"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"အပ်ပလီကေးရှင်း အချက်အလက်များ"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"ဖယ်ရှားမည်"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ဖယ်ထုတ်မည်"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"အက်ပ်အချက်အလက်များ"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"အတိုကောက်မှတ်သားမှုများအား ထည့်သွင်းခြင်း"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"အသုံးပြုသူ လုပ်ဆောင်မှုမရှိပဲ အပ်ပလီကေးရှင်းကို အတိုကောက်မှတ်သားမှုများ ပြုလုပ်ခွင့် ပေးခြင်း"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ဖတ်ခြင်း"</string>
@@ -51,33 +53,29 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"စဖွင့်သတ်မှတ်ရန်"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"အမည်မရှိအကန့်"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ပိတ်ထားသည်"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"စာမျက်နှာ %1$d မှ %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"ပင်မစာမျက်နှာ %1$d မှ %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"ပင်မမျက်နှာပြင် စာမျက်နှာသစ်"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"မင်္ဂလာပါ"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"အပ်ပလီကေးရှင်းပုံညွှန်းများကို ကူးယူပါ"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"ပင်မစာမျက်နှာအဟောင်းမှ ပုံညွှန်းများ နှင့် အကန့်များကို ယူလာပါမလား"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPY ICONS"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"START FRESH"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"နောက်ခံများ၊ ဝီဂျက်များ&amp; ဆက်တင်များ"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"နောက်ခံကို စိတ်တိုင်းကျ ပြုလုပ်ရန် ထိလျက် &amp; ကိုင်ထားပါ"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ရပြီ"</string>
     <string name="folder_opened" msgid="94695026776264709">"ဖွင့်ထားသောအကန့်, <xliff:g id="WIDTH">%1$d</xliff:g> နှင့် <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"အကန့်ကို ပိတ်ရန် ဖိကိုင်ပါ"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"အမည်ပြောင်းခြင်း အတည်ပြုရန် ဖိကိုင်ပါ"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"ဖိုင်တွဲကို ပိတ်ရန် တို့ပါ"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"အမည်ပြောင်းခြင်းကို သိမ်းဆည်းရန် တို့ပါ"</string>
     <string name="folder_closed" msgid="4100806530910930934">"ပိတ်ထားသောအကန့်"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"ပြောင်းလဲလိုက်သော အကန့်အမည် <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"အကန့်အမည်: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ဝဒ်ဂျက်များ"</string>
+    <string name="widget_button_text" msgid="2880537293434387943">"ဝိဂျက်များ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"နောက်ခံများ"</string>
-    <string name="settings_button_text" msgid="8119458837558863227">"အပြင်အဆင်များ"</string>
+    <string name="settings_button_text" msgid="8119458837558863227">"ဆက်တင်များ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"သင့်စီမံခန့်ခွဲသူက ပိတ်လိုက်ပါသည်"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"လှည့်ရန် ခွင့်ပြုမည်"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"ခြုံငုံသုံးသပ်ချက်"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"ပင်မစာမျက်နှာလှည့်ခြင်းကို ခွင့်ပြုပါ"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"ဖုန်းကိုလှည့်ထားစဉ်"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"လက်ရှိ မြင်ကွင်းဆက်တင်တွင် မြင်ကွင်းကို လှည့်ခွင့်မပေးပါ"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"မသိရ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ဖယ်ရှားရန်"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ရှာဖွေရန်"</string>
-    <string name="abandoned_promises_title" msgid="7096178467971716750">"App မတပ်ဆင်ရသေးပါ"</string>
-    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ဤအိုင်ကွန်အတွက် app အားမထည့်သွင်းထားပါ။ You can remove it, or search for the app and install it manually."</string>
+    <string name="abandoned_promises_title" msgid="7096178467971716750">"အက်ပ်မတပ်ဆင်ရသေးပါ"</string>
+    <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ဤအိုင်ကွန်အတွက် အက်ပ်အားမထည့်သွင်းထားပါ။ You can remove it, or search for the အက်ပ်and install it manually."</string>
     <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ဒေါင်းလုဒ်လုပ်နေသည်၊ <xliff:g id="PROGRESS">%2$s</xliff:g> ပြီးပါပြီ"</string>
     <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ကိုထည့်သွင်းရန်စောင့်နေသည်"</string>
     <string name="action_add_to_workspace" msgid="8902165848117513641">"ပင်မမျက်နှာစာသို့ ထည့်ပါ"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"အကျယ်အား လျှော့ပါ"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"အမြင့်အား လျှော့ပါ"</string>
     <string name="widget_resized" msgid="9130327887929620">"Widget အား အကျယ် <xliff:g id="NUMBER_0">%1$s</xliff:g> အမြင့် <xliff:g id="NUMBER_1">%2$s</xliff:g> အရွယ်အစားပြန်လည်ချိန်ညှိပြီး၏"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"ဖြတ်လမ်းများ"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> အတွက် အမြန်နည်း <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ခု"</string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 934a5d0..449094d 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -26,20 +26,22 @@
     <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>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Moduler er deaktivert i sikker modus"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Snarveien er ikke tilgjengelig"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Trykk og hold inne for å plukke opp en modul."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dobbelttrykk og hold inne for å velge en modul eller bruke tilpassede handlinger."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Søk i apper"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d bredde x %2$d høyde"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Søk i apper"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Laster inn apper …"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Fant ingen apper som samsvarer med «<xliff:g id="QUERY">%1$s</xliff:g>»"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Gå til <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Søk etter flere apper"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Denne startsiden er full."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritter-skuffen er full"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Apper"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"App-liste"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Startside"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Fjern"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Avinstaller"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"App-info"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Fjern"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Avinstaller"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Info om appen"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installere snarveier"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Gir apper tillatelse til å legge til snarveier uten innblanding fra brukeren."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"lese startsideinnstillinger og -snarveier"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Konfigurering"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dette er en systemapp som ikke kan avinstalleres."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Mappe uten navn"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Slo av <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Side %1$d av %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Startside %1$d av %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Ny side på startskjermen"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Velkommen"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopiér appikonene dine"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Vil du importere ikoner og mapper fra dine gamle startsider?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPIÉR IKONENE"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"START PÅ NYTT"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Bakgrunner, moduler og innstillinger"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Trykk og hold på bakgrunnen for å tilpasse den"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"SKJØNNER"</string>
     <string name="folder_opened" msgid="94695026776264709">"Mappen er åpnet – <xliff:g id="WIDTH">%1$d</xliff:g> ganger <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Trykk for å lukke mappen"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Trykk for å lagre det nye navnet"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Trykk for å lukke mappen"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Trykk for å lagre det nye navnet"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Mappen ble lukket"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Mappen heter nå <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunner"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Innstillinger"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratoren har slått av funksjonen"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Tillat rotasjon"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Oversikt"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Tillat rotasjon av startskjermen"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Når telefonen roteres"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Med den nåværende skjerminnstillingen støttes ikke rotasjon"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Ukjent"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjern"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Søk"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Reduser bredden"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Reduser høyden"</string>
     <string name="widget_resized" msgid="9130327887929620">"Størrelsen på modulen er endret til bredde <xliff:g id="NUMBER_0">%1$s</xliff:g> og høyde <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Snarveier"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> snarveier for <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index f4f46d8..8accfcc 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"सर्टकट उपलब्ध छैन"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"एउटा विजेटलाई टिप्नको लागि टच गरेर होल्ड गर्नुहोस्।"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"विजेटलाई छान्न वा अनुकूलन कार्यहरू प्रयोग गर्न डबल ट्याप गरी होल्ड गर्नुहोस्।"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"अनुप्रयोगहरू खोज्नुहोस्..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d चौडाइ गुणा %2$d उचाइ"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"अनुप्रयोगहरू खोज्नुहोस्"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"अनुप्रयोगहरू लोड गरिँदै..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" सँग मिल्दो कुनै अनुप्रयोगहरू फेला परेनन्"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> मा जानुहोस्"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"थप अनुप्रयोगहरू खोज्नुहोस्"</string>
     <string name="out_of_space" msgid="4691004494942118364">"यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"मनपर्ने ट्रे अब कुनै ठाँउ छैन"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"अनुप्रयोगहरू"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"अनुप्रयोगको सूची"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"गृह"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"हटाउनुहोस्"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"स्थापना हटाउनुहोस्"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"अनुप्रयोग जानकारी"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"हटाउनुहोस्"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"विस्थापित गर्नुहोस्"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"अनुप्रयोग जानकारी"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा अनुप्रयोगलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"गृह सेटिङहरू र सर्टकटहरू पढ्नुहोस्"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"सेटअप"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"यो प्रणाली अनुप्रयोग हो र यसलाई स्थापना रद्द गर्न सकिँदैन।"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"बेनाम फोल्डर"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"असक्षम पारिएको <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"पृष्ठ %2$d को %1$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"गृह स्क्रिन %1$d को %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"नयाँ गृह स्क्रिन पृष्ठ"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"स्वागतम"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"तपाईँको अनुप्रयोग आईकनको प्रतिलिप गर्नुहोस्"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"आफ्नो पुरानो गृह स्क्रीनबाट अाईकन र फोल्डरहरू आयात गर्नुहोस्?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"ICONS प्रतिलिप गर्नुहोस्"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"START FRESH"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"वालपेपरहरू, विजेट; सेटिङहरू"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"छुनुहोस् ; अनुकूलन पृष्ठभूमि होल्ड गर्नुहोस्"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"बुझियो"</string>
     <string name="folder_opened" msgid="94695026776264709">"फोल्डर खुल्यो <xliff:g id="WIDTH">%1$d</xliff:g> बाट <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"फोल्डर बन्द गर्नको लागि टच गर्नुहोस्"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"पुन: नामाकरण बचत गर्न टच गर्नुहोस्।"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"फोल्डरलाई बन्द गर्न ट्याप गर्नुहोस्"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"पुनःनामाकरणलाई सुरक्षित गर्न ट्याप गर्नुहोस्"</string>
     <string name="folder_closed" msgid="4100806530910930934">"फोल्डर बन्द भयो"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"फोल्डर <xliff:g id="NAME">%1$s</xliff:g> मा पुनःनामाकरण गरियो।"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"वालपेपरहरु"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"सेटिंङहरू"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"तपाईँको प्रशासकद्वारा असक्षम गरिएको"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"परिक्रमणलाई अनुमति दिनुहोस्"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"परिदृश्य"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"गृह स्क्रिनलाई घुम्ने अनुमति दिनुहोस्"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"फोनलाई घुमाइँदा"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"हालको प्रदर्शन सम्बन्धी सेटिङले घुमाउने सुविधालाई अनुमति दिँदैन"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"हटाउनुहोस्"</string>
     <string name="abandoned_search" msgid="891119232568284442">"खोजी गर्नुहोस्"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"चौडाइ घटाउनुहोस्"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"उँचाइ घटाउनुहोस्"</string>
     <string name="widget_resized" msgid="9130327887929620">"विजेट चौडाइ <xliff:g id="NUMBER_0">%1$s</xliff:g> उचाइ <xliff:g id="NUMBER_1">%2$s</xliff:g> मा पुनः आकार मिलाइयो"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"सर्टकटहरू"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> का <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> सर्टकटहरू"</string>
 </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 58ab411..0134ae1 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"App is niet beschikbaar"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Gedownloade app uitgeschakeld in veilige modus"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets uitgeschakeld in Veilige modus"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Snelkoppeling is niet beschikbaar"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Blijf aanraken om een widget toe te voegen."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dubbeltik en blijf aanraken om een widget toe te voegen of aangepaste acties te gebruiken."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Apps zoeken…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breed en %2$d hoog"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Apps zoeken"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Apps laden…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Er zijn geen apps gevonden die overeenkomen met \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Ga naar <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Zoeken naar meer apps"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Er is geen ruimte meer op dit startscherm."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Geen ruimte meer in het vak \'Favorieten\'"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Lijst met apps"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Startpagina"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Verwijderen"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Deïnstalleren"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"App-info"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Verwijderen"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Deïnstalleren"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"App-info"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Snelle links instellen"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Een app toestaan snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"instellingen en snelkoppelingen op de startpagina lezen"</string>
@@ -51,28 +53,24 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Configuratie"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Dit is een systeemapp die niet kan worden verwijderd."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Naamloze map"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> is uitgeschakeld"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d van %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Startscherm %1$d van %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nieuwe startschermpagina"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Welkom"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Je app-pictogrammen kopiëren"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Pictogrammen en mappen importeren uit je oude startschermen?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"PICTOGRAMMEN KOPIËREN"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"OPNIEUW BEGINNEN"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Achtergronden, widgets en instellingen"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Blijf de achtergrond aanraken om deze aan te passen"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
     <string name="folder_opened" msgid="94695026776264709">"Map geopend, <xliff:g id="WIDTH">%1$d</xliff:g> bij <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Raak dit aan om de map te sluiten"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Raak dit aan om de gewijzigde naam op te slaan"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Tik om de map te sluiten"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tik om de gewijzigde naam op te slaan"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Map gesloten"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"De naam van de map is gewijzigd in <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Map: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
-    <string name="wallpaper_button_text" msgid="8404103075899945851">"Achtergronden"</string>
+    <string name="wallpaper_button_text" msgid="8404103075899945851">"Achtergrond"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Instellingen"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Uitgeschakeld door je beheerder"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Draaien toestaan"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Overzicht"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Draaien van startscherm toestaan"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Wanneer de telefoon gedraaid is"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Huidige scherminstelling staat draaien niet toe"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Verwijderen"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Zoeken"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Breedte verkleinen"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Hoogte verkleinen"</string>
     <string name="widget_resized" msgid="9130327887929620">"Formaat van widget gewijzigd in breedte <xliff:g id="NUMBER_0">%1$s</xliff:g> en hoogte <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Snelkoppelingen"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> snelkoppelingen voor <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
index 1b3243b..3be9bec 100644
--- a/res/values-pa-rIN/strings.xml
+++ b/res/values-pa-rIN/strings.xml
@@ -25,54 +25,52 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"ਸ਼ਾਰਟਕੱਟ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਛੋਹਵੋT &amp; ਹੋਲਡ ਕਰੋ।"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"ਡਬਲ-ਟੈਪ &amp; ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਹੋਲਡ ਕਰੋ ਅਤੇ ਕਸਟਮ ਕਿਰਿਆਵਾਂ ਵਰਤੋ।"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ਐਪਾਂ ਖੋਜੋ…"</string>
-    <string name="all_apps_loading_message" msgid="7557140873644765180">"ਐਪਸ ਲੋਡ ਕਰ ਰਿਹਾ ਹੈ..."</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ਨਾਲ ਮਿਲਦੀ ਕੋਈ ਵੀ ਐਪਸ ਨਹੀਂ ਲੱਭੀਆਂ"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> ਤੇ ਜਾਓ"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ਚੌੜਾਈ ਅਤੇ %2$d ਲੰਬਾਈ"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ਐਪਸ ਖੋਜੋ"</string>
+    <string name="all_apps_loading_message" msgid="7557140873644765180">"ਐਪਾਂ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ..."</string>
+    <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ਨਾਲ ਮਿਲਦੀਆਂ ਕੋਈ ਵੀ ਐਪਾਂ ਨਹੀਂ ਮਿਲੀਆਂ"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"ਹੋਰ ਐਪਾਂ ਖੋਜੋ"</string>
     <string name="out_of_space" msgid="4691004494942118364">"ਇਸ ਹੋਮ ਸਕ੍ਰੀਨ ਲਈ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ ਹੈ।"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ਮਨਪਸੰਦ ਟ੍ਰੇ ਵਿੱਚ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ।"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"ਐਪਸ"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"ਐਪ ਸੂਚੀ"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"ਹੋਮ"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"ਹਟਾਓ"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"ਅਣਇੰਸਟੌਲ ਕਰੋ"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"ਐਪ ਜਾਣਕਾਰੀ"</string>
-    <string name="permlab_install_shortcut" msgid="5632423390354674437">"ਸ਼ੌਰਟਕਟ ਇੰਸਟੌਲ ਕਰੋ"</string>
-    <string name="permdesc_install_shortcut" msgid="923466509822011139">"ਇੱਕ ਐਪ ਨੂੰ ਉਪਭੋਗਤਾ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ੌਰਟਕਟ ਜੋੜਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
-    <string name="permlab_read_settings" msgid="1941457408239617576">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ੌਰਟਕਟ ਪੜ੍ਹੋ"</string>
-    <string name="permdesc_read_settings" msgid="5833423719057558387">"ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ੌਰਟਕਟ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
-    <string name="permlab_write_settings" msgid="3574213698004620587">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ੌਰਟਕਟ ਲਿਖੋ"</string>
-    <string name="permdesc_write_settings" msgid="5440712911516509985">"ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ੌਰਟਕਟ ਬਦਲਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"ਹਟਾਓ"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ਸਥਾਪਨਾ ਰੱਦ ਕਰੋ"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"ਐਪ ਜਾਣਕਾਰੀ"</string>
+    <string name="permlab_install_shortcut" msgid="5632423390354674437">"ਸ਼ਾਰਟਕੱਟ ਇੰਸਟੌਲ ਕਰੋ"</string>
+    <string name="permdesc_install_shortcut" msgid="923466509822011139">"ਇੱਕ ਐਪ ਨੂੰ ਉਪਭੋਗਤਾ ਦੇ ਦਖ਼ਲ ਤੋਂ ਬਿਨਾਂ ਸ਼ਾਰਟਕੱਟ ਜੋੜਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
+    <string name="permlab_read_settings" msgid="1941457408239617576">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹੋ"</string>
+    <string name="permdesc_read_settings" msgid="5833423719057558387">"ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਪੜ੍ਹਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
+    <string name="permlab_write_settings" msgid="3574213698004620587">"ਹੋਮ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਲਿਖੋ"</string>
+    <string name="permdesc_write_settings" msgid="5440712911516509985">"ਐਪ ਨੂੰ ਹੋਮ ਵਿੱਚ ਸੈਟਿੰਗਾਂ ਅਤੇ ਸ਼ਾਰਟਕੱਟ ਬਦਲਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ।"</string>
     <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਫੋਨ ਕਾਲਾਂ ਕਰਨ ਦੀ ਆਗਿਆ ਨਹੀਂ ਹੈ"</string>
     <string name="gadget_error_text" msgid="6081085226050792095">"ਵਿਜੇਟ ਲੋਡ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ"</string>
-    <string name="gadget_setup_text" msgid="8274003207686040488">"ਸੈਟਅਪ"</string>
+    <string name="gadget_setup_text" msgid="8274003207686040488">"ਸਥਾਪਤ ਕਰੋ"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ਇਹ ਇੱਕ ਸਿਸਟਮ ਐਪ ਹੈ ਅਤੇ ਇਸਨੂੰ ਅਣਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"ਬਿਨਾਂ ਨਾਮ ਦਿੱਤਾ ਫੋਲਡਰ"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"ਸਫ਼ਾ %2$d ਦਾ %1$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"ਹੋਮ ਸਕ੍ਰੀਨ %2$d ਦੀ %1$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"ਨਵਾਂ ਹੋਮ ਸਕ੍ਰੀਨ ਸਫ਼ਾ"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"ਸੁਆਗਤ ਹੈ"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"ਆਪਣੇ ਐਪ ਆਈਕਨਾਂ ਨੂੰ ਕਾਪੀ ਕਰੋ"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"ਕੀ ਆਪਣੀਆਂ ਪੁਰਾਣੀਆਂ ਹੋਮ ਸਕ੍ਰੀਨਾਂ ਤੋਂ ਆਈਕਨਾਂ ਅਤੇ ਫੋਲਡਰ ਆਯਾਤ ਕਰਨੇ ਹਨ?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"ਆਈਕਨਾਂ ਨੂੰ ਕਾਪੀ ਕਰੋ"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ਤਾਜ਼ਾ ਸ਼ੁਰੂ ਕਰੋ"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"ਵਾਲਪੇਪਰ, ਵਿਜੇਟ, &amp; ਸੈਟਿੰਗਾਂ"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"ਅਨੁਕੂਲ ਕਰਨ ਲਈ ਪਿਛੋਕੜ ਛੋਹਵੋ &amp; ਹੋਲਡ ਕਰੋ"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ਸਮਝ ਗਿਆ"</string>
     <string name="folder_opened" msgid="94695026776264709">"ਫੋਲਡਰ ਖੋਲ੍ਹਿਆ, <xliff:g id="WIDTH">%1$d</xliff:g> ਬਾਇ <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"ਫੋਲਡਰ ਬੰਦ ਕਰਨ ਲਈ ਛੋਹਵੋ"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"ਮੁੜ ਨਾਮ ਦਿਓ ਨੂੰ ਸੁਰੱਖਿਅਤ ਕਰਨ ਲਈ ਛੋਹਵੋ"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"ਫੋਲਡਰ ਬੰਦ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"ਬਦਲੇ ਗਏ ਨਾਮ ਨੂੰ ਰੱਖਿਅਤ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
     <string name="folder_closed" msgid="4100806530910930934">"ਫੋਲਡਰ ਬੰਦ ਕੀਤਾ"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"ਫੋਲਡਰ ਨੂੰ <xliff:g id="NAME">%1$s</xliff:g> ਮੁੜ ਨਾਮ ਦਿੱਤਾ ਗਿਆ"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
-    <string name="widget_button_text" msgid="2880537293434387943">"ਵਿਜੇਟਸ"</string>
+    <string name="widget_button_text" msgid="2880537293434387943">"ਵਿਜਿਟ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ਵਾਲਪੇਪਰ"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"ਸੈਟਿੰਗਾਂ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"ਰੋਟੇਸ਼ਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"ਰੂਪ-ਰੇਖਾ"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"ਮੁੱਖ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁੰਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"ਜਦੋਂ ਫ਼ੋਨ ਘੁੰਮਾਇਆ ਜਾਂਦਾ ਹੈ"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ਵਰਤਮਾਨ ਡਿਸਪਲੇ ਸੈਟਿੰਗ ਘੁੰਮਾਉਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੰਦੀ ਹੈ"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"ਅਗਿਆਤ"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ਹਟਾਓ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ਖੋਜੋ"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"ਚੌੜਾਈ ਘਟਾਓ"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"ਉਂਚਾਈ ਘਟਾਓ"</string>
     <string name="widget_resized" msgid="9130327887929620">"ਵਿਜੈਟ ਨੂੰ ਚੌੜਾਈ <xliff:g id="NUMBER_0">%1$s</xliff:g> ਉਂਚਾਈ <xliff:g id="NUMBER_1">%2$s</xliff:g> ਨੂੰ ਮੁੜ ਆਕਾਰ ਦਿੱਤਾ"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"ਸ਼ਾਰਟਕੱਟ"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> ਲਈ <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ਸ਼ਾਰਟਕੱਟ"</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 43e4947..feb2a36 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Aplikacja niedostępna"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Pobrana aplikacja została wyłączona w trybie awaryjnym"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widżety są wyłączone w trybie bezpiecznym"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Skrót nie jest dostępny"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Aby dodać widżet, kliknij go i przytrzymaj."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Kliknij dwukrotnie i przytrzymaj, by wybrać widżet lub użyć działań niestandardowych."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Szukaj w aplikacjach…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Szerokość %1$d, wysokość %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Szukaj w aplikacjach"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Wczytuję aplikacje…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nie znaleziono aplikacji pasujących do zapytania „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Otwórz <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Wyszukaj więcej aplikacji"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Brak miejsca na tym ekranie głównym."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Brak miejsca w Ulubionych"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacje"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Lista aplikacji"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Ekran główny"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Usuń"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Odinstaluj"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Informacje o aplikacji"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Usuń"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinstaluj"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"O aplikacji"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalowanie skrótów"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Pozwala aplikacji dodawać skróty bez interwencji użytkownika."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"odczytywanie ustawień i skrótów na ekranie głównym"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Konfiguracja"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"To aplikacja systemowa i nie można jej odinstalować."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Folder bez nazwy"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest wyłączona"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Strona %1$d z %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ekran główny %1$d z %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nowa strona ekranu głównego"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Witamy"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopiuj ikony aplikacji"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Zaimportować ikony i foldery ze starych ekranów głównych?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPIUJ IKONY"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ZACZNIJ OD NOWA"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Tapety, widżety i ustawienia"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Kliknij i przytrzymaj tło, by dostosować"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
     <string name="folder_opened" msgid="94695026776264709">"Folder otwarty, <xliff:g id="WIDTH">%1$d</xliff:g> na <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Kliknij, by zamknąć folder"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Kliknij, by zapisać zmianę nazwy"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Kliknij, by zamknąć folder"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Kliknij, by zapisać nową nazwę"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Folder zamknięty"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Nazwa folderu zmieniona na <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Ustawienia"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Funkcja wyłączona przez administratora"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Zezwól na obrót"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Przegląd"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Zezwalaj na obrót ekranu głównego"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Po obróceniu telefonu"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Obecne ustawienia wyświetlania nie pozwalają na obrót ekranu"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Brak informacji"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Usuń"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Szukaj"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Zmniejsz szerokość"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Zmniejsz wysokość"</string>
     <string name="widget_resized" msgid="9130327887929620">"Szerokość i wysokość widżetu zmieniła się na <xliff:g id="NUMBER_0">%1$s</xliff:g> x <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Skróty"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"Skróty aplikacji <xliff:g id="APP_NAME">%2$s</xliff:g>: <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>"</string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 6338003..6d684fa 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"A aplicação não está disponível"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicação transferida desativada no Modo de segurança"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets desativados no Modo de segurança"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"O atalho não está disponível"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Prima sem soltar para escolher um widget."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toque duas vezes sem soltar para escolher um widget ou utilize ações personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Pesquisar aplicações..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largura por %2$d de altura"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pesquisar aplicações"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"A carregar aplicações..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Não foram encontradas aplic. que correspondam a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Aceder a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pesquisar mais aplicações"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Sem espaço suficiente neste Ecrã principal."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Não existe mais espaço no tabuleiro de Favoritos"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplicações"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Lista de aplicações"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Ecrã principal"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Remover"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalar"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Informações da aplicação"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Remover"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Inf. da aplicação"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a uma aplicação adicionar atalhos sem a intervenção do utilizador."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"ler definições e atalhos do Ecrã Principal"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Configuração"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"É uma aplicação de sistema e não pode ser desinstalada."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Pasta sem nome"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desativado"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ecrã principal %1$d de %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova página do ecrã principal"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Bem-vindo(a)"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Copiar ícones das aplicações"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Importar ícones e pastas dos ecrãs principais antigos?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPIAR ÍCONES"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"COMEÇAR DO INÍCIO"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Imagens de fundo, widgets e definições"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Toque sem soltar no fundo para personalizar"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"COMPREENDI"</string>
     <string name="folder_opened" msgid="94695026776264709">"Pasta aberta, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Toque para fechar a pasta"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Toque para guardar o nome novo"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Tocar para fechar a pasta"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tocar para guardar o nome novo"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Pasta fechada"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Nome de pasta alterado para <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Imagens de fundo"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Definições"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativada pelo administrador"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Permitir rotação"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Vista geral"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotação do ecrã principal"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Quando o telemóvel é rodado"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"A definição de visualização atual não permite a rotação"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remover"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Pesquisar"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Diminuir largura"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Diminuir altura"</string>
     <string name="widget_resized" msgid="9130327887929620">"Widget redimensionado para a largura <xliff:g id="NUMBER_0">%1$s</xliff:g>, altura <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Atalhos"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> atalhos para a aplicação <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index a8562a4..82a756f 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -26,20 +26,22 @@
     <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>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets desativados no modo de segurança"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"O atalho não está disponível"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Toque e pressione para selecionar um widget."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toque duas vezes e segure para selecionar um widget ou usar ações personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Pesquisar apps..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largura por %2$d de altura"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pesquisar apps"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Carregando apps…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nenhum app encontrado que corresponda a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Ir para <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Pesquisar mais apps"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Não há mais espaço na tela inicial."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Sem espaço na bandeja de favoritos"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Lista de apps"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Início"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Remover"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalar"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Informações do app"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Remover"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Desinstalar"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Informações do app"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que um app adicione atalhos sem intervenção do usuário."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"ler configurações e atalhos da tela inicial"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Configuração"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Este é um app do sistema e não pode ser desinstalado."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Pasta sem nome"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desativado"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Tela inicial %1$d de %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova página na tela inicial"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Bem-vindo"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Copiar ícones de apps"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Importar ícones e pastas de suas telas iniciais antigas?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPIAR ÍCONES"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"COMEÇAR DO ZERO"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Plano de fundo, widgets e configurações"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Toque no segundo plano e mantenha pressionado para personalizar"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ENTENDI"</string>
     <string name="folder_opened" msgid="94695026776264709">"Pasta aberta, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Toque para fechar a pasta"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Toque para salvar o novo nome"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Toque para fechar a pasta"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Toque para salvar o novo nome"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Pasta fechada"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Pasta renomeada para <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Planos de fundo"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Configurações"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativado pelo administrador"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Permitir rotação"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Visão geral"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotação da tela inicial"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Quando o smartphone for girado"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"A configuração atual de exibição não permite rotação"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remover"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Pesquisar"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Diminuir largura"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Diminuir altura"</string>
     <string name="widget_resized" msgid="9130327887929620">"Widget redimensionado para a largura <xliff:g id="NUMBER_0">%1$s</xliff:g>, altura <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Atalhos"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> atalhos para <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index bc8b0ab..5905bcf 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Aplicația nu este disponibilă"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicația descărcată este dezactivată în modul de siguranță"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgeturile sunt dezactivate în modul de siguranță"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Comanda rapidă nu este disponibilă"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Atingeți lung un widget pentru a-l alege."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Atingeți de două ori și mențineți apăsat ca să alegeți un widget sau folosiți acțiuni personalizate."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Căutați aplicații…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lățime și %2$d înălțime"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Căutați aplicații"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Se încarcă aplicațiile..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nu s-a găsit nicio aplicație pentru „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Accesați <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Căutați mai multe aplicații"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Nu mai este loc pe acest Ecran de pornire."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Spațiu epuizat în bara Preferate"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplicații"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Lista de aplicații"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Ecran de pornire"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Eliminați"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Dezinstalați"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Informații despre aplicație"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Eliminați"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Dezinstalați"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Informații aplicație"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalează comenzi rapide"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite unei aplicații să adauge comenzi rapide fără intervenția utilizatorului."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"citește setări și comenzi rapide pentru ecranul de pornire"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Configurați"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Aceasta este o aplicație de sistem și nu poate fi dezinstalată."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Dosar fără nume"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"S-a dezactivat <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d din %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ecranul de pornire %1$d din %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Pagină nouă pe ecranul de pornire"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Bun venit"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Copiați pictogr. aplicațiilor"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Import. pictogr. și dosare de pe ecranele de pornire anter.?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPIAȚI PICTOGRAMELE"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"REÎNCEPEȚI"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Imagini de fundal, widgeturi și setări"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Atingeți lung fundalul pentru a-l personaliza"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"AM ÎNȚELES"</string>
     <string name="folder_opened" msgid="94695026776264709">"Dosar deschis, <xliff:g id="WIDTH">%1$d</xliff:g> pe <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Atingeți pentru a închide dosarul"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Atingeți pentru a salva redenumirea"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Atingeți pentru a închide dosarul"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Atingeți pentru a salva noul nume"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Dosar închis"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Dosar redenumit <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Dosar: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Imagini de fundal"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Setări"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dezactivată de administrator"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Permiteți rotirea"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Prezentare generală"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Permiteți rotirea ecranului de pornire"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Când telefonul este rotit"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Setarea actuală a afișajului nu permite rotirea"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Necunoscut"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminați"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Căutați"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Reduceți lățimea"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Reduceți înălțimea"</string>
     <string name="widget_resized" msgid="9130327887929620">"Widgetul a fost redimensionat la lățimea <xliff:g id="NUMBER_0">%1$s</xliff:g> și înălțimea <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Comenzi rapide"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> comenzi rapide pentru <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 4a2cb61..ad30c97 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"Ярлык недоступен"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Чтобы выбрать виджет, нажмите на значок и удерживайте его."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Чтобы выбрать виджет, нажмите на него дважды и не отпускайте или выполните предложенные действия."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d x %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Поиск приложений"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина %1$d, высота %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Поиск приложений"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Загрузка…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"По запросу \"<xliff:g id="QUERY">%1$s</xliff:g>\" ничего не найдено"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Искать другие приложения"</string>
     <string name="out_of_space" msgid="4691004494942118364">"На этом экране все занято"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"В разделе \"Избранное\" больше нет места"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Приложения"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Список приложений"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Главный экран"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Убрать"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Удалить"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"О приложении"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Убрать"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Удалить"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"О приложении"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"Создание ярлыков"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Приложение сможет самостоятельно добавлять ярлыки."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"Доступ к настройкам и ярлыкам главного экрана"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Настройка"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Это системное приложение, его нельзя удалить."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Папка без названия"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> отключено"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Стр. %1$d из %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Главные экран %1$d из %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Новый экран"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Добро пожаловать!"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Копировать значки приложений"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Импортировать значки и папки со старого главного экрана?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"КОПИРОВАТЬ ЗНАЧКИ"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ИСПОЛЬЗОВАТЬ СТАНДАРТНЫЙ МАКЕТ"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Обои, виджеты и настройки"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Чтобы выполнить настройку, коснитесь фона и удерживайте его"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ОК"</string>
     <string name="folder_opened" msgid="94695026776264709">"Папка открыта, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Нажмите, чтобы закрыть папку"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Нажмите, чтобы подтвердить переименование"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Нажмите, чтобы закрыть папку"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Нажмите, чтобы подтвердить переименование"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Папка закрыта"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Папка переименована в \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Обои"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Настройки"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Функция отключена администратором"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Разрешить автоповорот"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Обзор"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Разрешить поворачивать главный экран"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Когда телефон повернут"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"В настройках отключен поворот экрана"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Неизвестно"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Удалить"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Найти"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Уменьшить ширину"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Уменьшить высоту"</string>
     <string name="widget_resized" msgid="9130327887929620">"Изменен размер виджета: до <xliff:g id="NUMBER_0">%1$s</xliff:g> в ширину и <xliff:g id="NUMBER_1">%2$s</xliff:g> в высоту"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Ярлыки"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"Количество ярлыков для приложения \"<xliff:g id="APP_NAME">%2$s</xliff:g>\": <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>"</string>
 </resources>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index a072cee..1e0ed28 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"කෙටි මග ලබා ගත නොහැකිය"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"විජට් එක ස්පර්ශ කර අහුලා ගැනීමට අල්ලාගෙන සිටින්න."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"විජට් එකක් අහුලා ගැනීමට හෝ අභිරුචි ක්‍රියා කිරීමට ඩබල් ටැප් කර අල්ලා ගෙන සිටින්න."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"යෙදුම් සොයන්න..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"පළල %1$d උස %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"යෙදුම් සෙවීම"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"යෙදුම් පූරණය වෙමින්…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" සමග ගැළපෙන යෙදුම් හමු නොවිණි"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> වෙත යන්න"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"තව යෙදුම් සඳහා සොයන්න"</string>
     <string name="out_of_space" msgid="4691004494942118364">"මෙම මුල් පිටු තිරය මත තවත් අවසර නැත."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ප්‍රියතම දෑ ඇති තැටියේ තවත් ඉඩ නොමැත"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"යෙදුම්"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"යෙදුම් ලැයිස්තුව"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"මුල් පිටුව"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"ඉවත් කරන්න"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"අස්ථාපනය කරන්න"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"යෙදුම් තොරතුරු"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"ඉවත් කරන්න"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"අස්ථාපනය කරන්න"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"යෙදුම් තොරතුරු"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"කෙටිමං ස්ථාපනය කරන්න"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"පරිශීලක මැදිහත්වීමෙන් තොරව කෙටිමං එක් කිරීමට යෙදුමකට අවසර දෙයි."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"මුල් පිටු සැකසීම් සහ කෙටිමං කියවන්න"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"ස්ථාපනය කරන්න"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"මෙය පද්ධති යෙදුමක් වන අතර අස්ථාපනය කළ නොහැක."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"නම් නොකළ ෆෝල්ඩරය"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> අබල කෙරිණි"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$d හි %1$d පිටුව"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"මුල් පිටු තිරය %2$d හි %1$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"නව මුල් පිටුව"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"සාදරයෙන් පිළිගනිමු"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"ඔබේ යෙදුම් නිරූපක පිටපත් කිරීම"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"ඔබගේ පැරණි මුල් තිර වල නිරූපක සහ ෆෝල්ඩර ආයාත කරන්නද?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"නිරූපක පිටපත් කරන්න"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"අලුතින් පටන්ගන්න"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"වෝල්පේපර, විජට්, සහ සැකසීම්"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"පසුබිම අභිරුචිකරණය කිරීමට ස්පර්ශ කර අල්ලා සිටින්න"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"තේරුණා"</string>
     <string name="folder_opened" msgid="94695026776264709">"ෆෝල්ඩරය විවෘත විය, <xliff:g id="WIDTH">%1$d</xliff:g> හි <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"ෆෝල්ඩරය වැසීමට ස්පර්ශ කරන්න"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"නැවත නම් කිරීම සුරැකීමට ස්පර්ශ කරන්න"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"ෆෝල්ඩරය වැසීමට තට්ටු කරන්න"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"යළි නම් කිරීම සුරැකීමට තට්ටු කරන්න"</string>
     <string name="folder_closed" msgid="4100806530910930934">"ෆෝල්ඩරය වසා ඇත"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"<xliff:g id="NAME">%1$s</xliff:g> වෙත ෆෝල්ඩරය නැවත නම් කෙරිණි"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"ෆෝල්ඩරය: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"වෝල්පේපර"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"සැකසීම්"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ඔබගේ පරිපාලක විසින් අබල කරන ලදී"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"කරකැවීමට ඉඩ දෙන්න"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"දළ විශ්ලේෂණය"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"මුල් පිටු තිරය කරකැවීමට ඉඩ දෙන්න"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"දුරකථනය කරකවන විට"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"වත්මන් සංදර්ශක සැකසීම් කරකැවීමට සහාය නොදක්වයි"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"නොදනී"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ඉවත් කරන්න"</string>
     <string name="abandoned_search" msgid="891119232568284442">"සොයන්න"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"පළල අඩු කරන්න"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"උස අඩු කරන්න"</string>
     <string name="widget_resized" msgid="9130327887929620">"විජට් පළල <xliff:g id="NUMBER_0">%1$s</xliff:g> උස <xliff:g id="NUMBER_1">%2$s</xliff:g> වෙත ප්‍රමාණකරණය කරන ලදි"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"කෙටිමං"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> සඳහා කෙටි මං <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>"</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 5fb421a..7210d79 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -26,20 +26,22 @@
     <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>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Miniaplikácie sú v núdzovom režime zakázané"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Skratky nie sú k dispozícii"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Miniaplikáciu pridáte stlačením a podržaním."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Miniaplikáciu pridáte dvojitým klepnutím a pridržaním alebo pomocou vlastných akcií."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Vyhľadávanie v aplikáciách…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"šírka %1$d, výška %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Hľadať aplikácie"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Načítavajú sa aplikácie..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nenašli sa žiadne aplikácie zodpovedajúce dopytu <xliff:g id="QUERY">%1$s</xliff:g>"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Prejsť na dopyt <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Hľadať ďalšie aplikácie"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Na tejto ploche už nie je miesto"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Na paneli Obľúbené položky už nie je miesto"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplikácie"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Zoznam aplikácií"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Domovská stránka"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Odstrániť"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Odinštalovať"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"O aplikácii"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Odstrániť"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odinštalovať"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Info o aplikácii"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"inštalovať odkazy"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Povoľuje aplikácii pridať odkazy bez zásahu používateľa."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"čítanie nastavení a odkazov plochy"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Nastavenie"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Toto je systémová aplikácia a nedá sa odinštalovať."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Nepomenovaný priečinok"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je zakázaná"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Stránka %1$d z %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Plocha %1$d z %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nová stránka plochy"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Vitajte!"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopírovanie ikon aplikácií"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Chcete importovať ikony a priečinky zo starých plôch?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"SKOPÍROVAŤ IKONY"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ZAČAŤ ODZNOVA"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Pozadia, miniaplikácie a nastavenia"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Ak si chcete pozadie prispôsobiť, klepnite naň a podržte ho"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"DOBRE"</string>
     <string name="folder_opened" msgid="94695026776264709">"Otvorený priečinok, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Dotykom zavriete priečinok"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Dotykom premenovanie uložíte"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Priečinok zavriete klepnutím"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Nový názov uložíte klepnutím"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Priečinok je uzavretý"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Priečinok bol premenovaný na <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Nastavenia"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázané vaším správcom"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Povoliť otáčanie"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Prehľad"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Povoliť otáčanie plochy"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Pri otočení telefónu"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Aktuálne nastavenie obrazovky nepovoľuje otáčanie"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznáme"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstrániť"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Vyhľadať"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Znížiť šírku"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Znížiť výšku"</string>
     <string name="widget_resized" msgid="9130327887929620">"Veľkosť miniaplikácie bola zmenená na <xliff:g id="NUMBER_0">%1$s</xliff:g> x <xliff:g id="NUMBER_1">%2$s</xliff:g> (šírka x výška)"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Skratky"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"Počet skratiek aplikácie <xliff:g id="APP_NAME">%2$s</xliff:g>: <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>"</string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 57a89c7..b55cb0d 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -26,20 +26,22 @@
     <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>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Pripomočki so onemogočeni v varnem načinu"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Bližnjica ni na voljo"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Za izbiro pripomočka se ga dotaknite in pridržite."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Če želite izbrati pripomoček ali uporabiti dejanja po meri, se ga dvakrat dotaknite in ga pridržite."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Iskanje po aplikacijah …"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, višina %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Iskanje po aplikacijah"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Nalaganje aplikacij …"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Ni aplikacij, ki bi ustrezale poizvedbi »<xliff:g id="QUERY">%1$s</xliff:g>«"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Odpri storitev <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Iskanje več aplikacij"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Na tem začetnem zaslonu ni več prostora."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"V vrstici za priljubljene ni več prostora"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacije"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Seznam aplikacij"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Začetni zaslon"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Odstrani"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Odstrani namestitev"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Podatki o aplikaciji"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Odstrani"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Odstrani"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Podatki o aplikaciji"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"namestitev bližnjic"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji dovoli dodajanje bližnjic brez posredovanja uporabnika."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"branje nastavitev in bližnjic na začetnem zaslonu"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Nastavitev"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"To je sistemska aplikacija in je ni mogoče odstraniti."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Neimenovana mapa"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogočena"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Stran %1$d od %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Začetni zaslon %1$d od %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova stran na začetnem zaslonu"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Pozdravljeni"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopiranje ikon aplikacij"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Želite uvoziti ikone in mape iz starih začetnih zaslonov?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPIRAJ IKONE"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"SVEŽ ZAČETEK"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Ozadja, pripomočki in nastavitve"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Za prilagajanje se dotaknite ozadja in ga pridržite"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"V REDU"</string>
     <string name="folder_opened" msgid="94695026776264709">"Mapa je odprta, <xliff:g id="WIDTH">%1$d</xliff:g> krat <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Dotaknite se, da zaprete mapo"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Dotaknite se, da shranite preimenovanje"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Dotaknite se, da zaprete mapo"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Dotaknite se, da shranite preimenovanje"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Mapa je zaprta"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Mapa je preimenovana v <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ozadja"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Nastavitve"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogočil skrbnik."</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Omogočanje zasuka"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Pregled"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Omogočanje sukanja začetnega zaslona"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Ko se telefon zasuka"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Trenutna nastavitev zaslona ne dovoljuje sukanja"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Neznano"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstrani"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Iskanje"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Zmanjšanje širine"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Zmanjšanje višine"</string>
     <string name="widget_resized" msgid="9130327887929620">"Velikost pripomočka je bila spremenjena na <xliff:g id="NUMBER_0">%1$s</xliff:g> širine in <xliff:g id="NUMBER_1">%2$s</xliff:g> višine"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Bližnjice"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"Št. bližnjic za aplikacijo <xliff:g id="APP_NAME">%2$s</xliff:g>: <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>"</string>
 </resources>
diff --git a/res/values-sq-rAL/strings.xml b/res/values-sq-rAL/strings.xml
index a530ff9..c4fbaf3 100644
--- a/res/values-sq-rAL/strings.xml
+++ b/res/values-sq-rAL/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Aplikacioni nuk mundësohet"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplikacioni i shkarkuar është i çaktivizuar në modalitetin e sigurt"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Miniaplikacionet janë të çaktivizuara në modalitetin e sigurt"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Shkurtorja nuk është e disponueshme"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Prek dhe mbaj shtypur për të zgjedhur një miniaplikacion."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Prek dy herë dhe mbaj shtypur për të zgjedhur një miniaplikacion ose për të përdorur veprimet e personalizuara."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Kërko për aplikacione..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d i gjerë me %2$d i lartë"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Kërko për aplikacione"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Po ngarkon aplikacionet..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Nuk u gjet asnjë aplikacion që përputhet me \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Shko te <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Kërko për më shumë aplikacione"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Nuk ka më hapësirë në këtë ekran bazë."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Nuk ka më hapësirë në tabakanë \"Të preferuarat\""</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacionet"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Lista e aplikacioneve"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Faqja kryesore"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Hiq"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Çinstalo"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Informacion mbi aplikacionin"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Hiqe"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Çinstalo"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Informacion mbi aplikacionin"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalo shkurtore"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Lejon një aplikacion të shtojë shkurtore pa ndërhyrjen e përdoruesit."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"lexo cilësimet dhe shkurtoret e ekranit bazë"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Konfiguro"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ky është aplikacion sistemi dhe nuk mund të çinstalohet."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Dosje e paemërtuar"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> u çaktivizua"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Faqja: %1$d nga gjithsej %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ekrani bazë: %1$d nga gjithsej %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Faqja e ekranit të ri kryesor"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Mirë se erdhe"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopjo ikonat e aplikacionit"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Të importohen ikona dhe dosje nga ekranet e vjetra bazë?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPJO IKONA"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"FILLO NGA E PARA"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Imazhe në sfond, miniaplikacione dhe cilësime"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Prek dhe mbaj shtypur sfondin për ta personalizuar."</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"E KUPTOVA!"</string>
     <string name="folder_opened" msgid="94695026776264709">"Dosja u hap, <xliff:g id="WIDTH">%1$d</xliff:g> me <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Prek për të afruar dosjen"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Prek për të ruajtur riemërtimin"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Trokit për të mbyllur dosjen"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Trokit për të ruajtur riemërtimin"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Dosja u mbyll"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Dosja u riemërtua në <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Imazhet e sfondit"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Cilësimet"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Çaktivizuar nga administratori"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Lejo rotacionin"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Përmbledhje"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Lejo rrotullimin e ekranit kryesor"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Kur telefoni rrotullohet"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Cilësimi aktuali i afishimit nuk lejon rrotullimin"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"I panjohur"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Hiq"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Kërko"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Zvogëlo gjerësinë"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Zvogëlo lartësinë"</string>
     <string name="widget_resized" msgid="9130327887929620">"Madhësia e miniaplikacionit u ndryshua me gjerësinë <xliff:g id="NUMBER_0">%1$s</xliff:g> dhe lartësinë <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Shkurtoret"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> shkurtesa për <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index cc3d6a2..67a63cf 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"Пречица није доступна"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Додирните и задржите да бисте изабрали виџет."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Двапут додирните и задржите да бисте изабрали виџет или користите прилагођене радње."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Претражите апликације..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"ширина од %1$d и висина од %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Претражите апликације"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Апликације се учитавају..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Није пронађена ниједна апликација за „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Иди на апликацију <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Претражи још апликација"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Нема више простора на овом почетном екрану."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Нема више простора на траци Омиљено"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Апликације"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Листа апликација"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Почетна"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Уклони"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Деинсталирај"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Информације о апликацији"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Уклони"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Деинсталирај"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Информ. о апликацији"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирање пречица"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозвољава апликацији да додаје пречице без интервенције корисника."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"читање подешавања и пречица на почетном екрану"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Подешавање"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ово је системска апликација и не може да се деинсталира."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Неименовани директоријум"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је онемогућена"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%1$d. страница од %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d. почетни екран од %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Нова страница почетног екрана"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Добро дошли"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Копирајте иконе апликација"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Увести иконе и директоријуме са старих почетних екрана?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"КОПИРАЈТЕ ИКОНЕ"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ПОЧНИТЕ ИСПОЧЕТКА"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Позадине, виџети и подешавања"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Додирните и задржите позадину да бисте прилагодили"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ВАЖИ"</string>
     <string name="folder_opened" msgid="94695026776264709">"Директоријум је отворен, <xliff:g id="WIDTH">%1$d</xliff:g> пута <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Додирните да бисте затворили директоријум"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Додирните да бисте сачували промену имена"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Додирните да бисте затворили директоријум"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Додирните да бисте сачували преименовање"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Директоријум је затворен"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Директоријум је преименован у <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Директоријум: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Позадине"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Подешавања"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администратор је онемогућио"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Дозволи ротацију"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Преглед"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволи ротацију почетног екрана"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Када се телефон ротира"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Актуелно подешавање приказа не дозвољава ротацију"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Уклони"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Претражи"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Смањи ширину"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Смањи висину"</string>
     <string name="widget_resized" msgid="9130327887929620">"Величина виџета је промењена на ширину <xliff:g id="NUMBER_0">%1$s</xliff:g> и висину <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Пречице"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> пречице(а) за <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 9eb5299..dfcc1b0 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Appen är inte tillgänglig"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Den hämtade appen inaktiverades i säkert läge"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Widgets är inaktiverade i felsäkert läge"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Genvägen är inte tillgänglig"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Tryck länge om du vill flytta en widget."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Tryck två gånger och håll kvar om du vill ta upp en widget eller använda anpassade åtgärder."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Sök efter appar …"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d bred gånger %2$d hög"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Sök efter appar"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Läser in appar …"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Det gick inte att hitta några appar som matchar <xliff:g id="QUERY">%1$s</xliff:g>"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Gå till <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Sök efter fler appar"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Det finns inte plats för mer på den här startskärmen."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritfältet är fullt"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Appar"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Applista"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Startskärm"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Ta bort"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Avinstallera"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Info om appen"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Ta bort"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Avinstallera"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Info om appen"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"installera genvägar"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Tillåter att en app lägger till genvägar utan åtgärd från användaren."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"läsa inställningar och genvägar för startsidan"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Konfiguration"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Det här är en systemapp som inte kan avinstalleras."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Namnlös mapp"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> har inaktiverats"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Sidan %1$d av %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Startskärmen %1$d av %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Ny sida på startskärmen"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Välkommen"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopiera appikoner"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Vill du importera ikoner och mappar från gamla startskärmar?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPIERA IKONER"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"BÖRJA OM"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Bakgrunder, widgetar och inställningar"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Tryck länge på bakgrunden om du vill anpassa den"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
     <string name="folder_opened" msgid="94695026776264709">"Mappen är öppen, <xliff:g id="WIDTH">%1$d</xliff:g> gånger <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Tryck om du vill stänga mappen"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Tryck om du vill spara det nya namnet"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Tryck om du vill stänga mappen"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Tryck om du vill spara namnändringen"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Mappen är stängd"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Mappen har bytt namn till <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Mapp: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunder"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Inställningar"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inaktiverat av administratören"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Tillåt rotering"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Översikt"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Tillåt rotering av startskärmen"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"När mobilen vrids"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Rotering tillåts inte i de nuvarande skärminställningarna"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Okänt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ta bort"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Sök"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Minska bredden"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Minska höjden"</string>
     <string name="widget_resized" msgid="9130327887929620">"Widgetens storlek har ändrats till: bredd <xliff:g id="NUMBER_0">%1$s</xliff:g>, höjd <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Genvägar"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> genvägar för <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 12954f4..03c8f06 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Programu haipatikani"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Programu iliyopakuliwa imezimwa katika Hali Salama"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Wijeti zimezimwa katika hali ya Usalama"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Hakuna njia ya mkato"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Gusa na ushikilie ili kuteua wijeti."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Gonga mara mbili na ushikilie ile uchague wijeti au utumie vitendo maalum."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Tafuta Programu..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Upana wa %1$d na kimo cha %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Tafuta Programu"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Inapakia Programu..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Haikupata programu zinazolingana na \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Nenda kwenye <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Tafuta programu zaidi"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Hakuna nafasi katika skrini hii ya Mwanzo."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Hakuna nafasi zaidi katika treya ya Vipendeleo"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Programu"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Orodha ya programu"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Mwanzo"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Ondoa"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Sakinusha"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Maelezo ya programu"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Ondoa"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Ondoa"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Maelezo ya programu"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"kuweka njia za mkato"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Huruhusu programu kuongeza njia za mkato bila mtumiaji kuingilia kati."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"soma mipangilio ya Mwanzo na njia za mkato"</string>
@@ -51,22 +53,15 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Sanidi"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Hii ni programu ya mfumo na haiwezi kuondolewa."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Folda isiyo na jina"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> imezimwa"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Ukurasa%1$d wa %2$d"</string>
     <!-- String.format failed for translation -->
     <!-- no translation found for workspace_scroll_format (8458889198184077399) -->
     <skip />
     <string name="workspace_new_page" msgid="257366611030256142">"Ukurasa mpya wa skrini ya kwanza"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Karibu"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Nakili ikoni za programu yako"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Je, ungependa kuingiza ikoni na folda kutoka kwenye skrini zako za Mwanzo za zamani?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"NAKILI IKONI"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ANZA UPYA"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Mandhari, wijeti, na mipangilio"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Gusa na ushikilie mandhari ili uweke mapendeleo"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"NIMEELEWA"</string>
     <string name="folder_opened" msgid="94695026776264709">"Folda imefunguliwa, <xliff:g id="WIDTH">%1$d</xliff:g> kwa <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Gusa ili ufunge folda"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Gusa ili uhifadhi jina jipya"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Gonga ili ufunge folda"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Gonga ili ubadilishe jina"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Folda imefungwa"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Folda imebadilishwa jina kuwa <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Folda: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -74,7 +69,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Mandhari"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Mipangilio"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Imezimwa na msimamizi wako"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Ruhusu kuzungusha"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Muhtasari"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Ruhusu kuzungusha skrini ya Kwanza"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Simu inapozungushwa"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Mipangilio ya sasa ya sehemu ya Onyesho hairuhusu kuzungusha"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Yasiyojulikana"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ondoa"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Tafuta"</string>
@@ -106,4 +104,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Punguza upana"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Punguza urefu"</string>
     <string name="widget_resized" msgid="9130327887929620">"Wijeti imepunguzwa hadi upana <xliff:g id="NUMBER_0">%1$s</xliff:g> urefu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Njia za mkato"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"Njia <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> za mkato za <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-sw600dp-land/dimens.xml b/res/values-sw600dp-land/dimens.xml
deleted file mode 100644
index 644c891..0000000
--- a/res/values-sw600dp-land/dimens.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 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.
--->
-
-<resources>
-<!-- QSB -->
-    <dimen name="toolbar_button_vertical_padding">12dip</dimen>
-    <dimen name="toolbar_button_horizontal_padding">20dip</dimen>
-
-<!-- Container -->
-     <dimen name="container_max_width">736dp</dimen>
-
-</resources>
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index a7345a7..eb9af97 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -1,7 +1,4 @@
 <resources>
     <bool name="is_tablet">true</bool>
     <bool name="allow_rotation">true</bool>
-
-<!-- DragController -->
-    <integer name="config_flingToDeleteMinVelocity">-1000</integer>
 </resources>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index 0756dc9..2838088 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -15,23 +15,15 @@
 -->
 
 <resources>
-    <!-- Container -->
-    <dimen name="container_min_margin">16dp</dimen>
-
-    <!-- All Apps -->
+<!-- All Apps -->
     <dimen name="all_apps_grid_view_start_margin">0dp</dimen>
     <dimen name="all_apps_grid_section_text_size">26sp</dimen>
-    <dimen name="all_apps_icon_top_bottom_padding">12dp</dimen>
     <dimen name="all_apps_background_canvas_width">850dp</dimen>
     <dimen name="all_apps_background_canvas_height">525dp</dimen>
-    <dimen name="all_apps_icon_width_gap">36dp</dimen>
 
-<!-- Cling -->
-    <dimen name="cling_migration_logo_height">400dp</dimen>
-    <dimen name="cling_migration_logo_width">274dp</dimen>
-    <dimen name="cling_migration_bg_size">600dp</dimen>
-    <dimen name="cling_migration_bg_shift">-300dp</dimen>
-    <dimen name="cling_migration_content_margin">64dp</dimen>
-    <dimen name="cling_migration_content_width">280dp</dimen>
+<!-- Widget tray -->
+    <dimen name="widget_section_indent">56dp</dimen>
 
+<!-- DragController -->
+    <dimen name="drag_flingToDeleteMinVelocity">-1000dp</dimen>
 </resources>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index b9e28a9..358d9b6 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -18,16 +18,7 @@
 <!-- All Apps -->
     <dimen name="all_apps_button_scale_down">8dp</dimen>
     <dimen name="all_apps_search_bar_height">54dp</dimen>
-    <dimen name="all_apps_icon_top_bottom_padding">14dp</dimen>
     <dimen name="all_apps_empty_search_message_top_offset">64dp</dimen>
     <dimen name="all_apps_empty_search_bg_top_offset">180dp</dimen>
 
-<!-- QSB -->
-    <dimen name="toolbar_button_vertical_padding">8dip</dimen>
-    <dimen name="toolbar_button_horizontal_padding">8dip</dimen>
-
-<!-- Cling -->
-    <dimen name="cling_migration_content_margin">96dp</dimen>
-    <dimen name="cling_migration_content_width">320dp</dimen>
-
 </resources>
diff --git a/res/values-sw720dp/styles.xml b/res/values-sw720dp/styles.xml
index e8b706e..674edaa 100644
--- a/res/values-sw720dp/styles.xml
+++ b/res/values-sw720dp/styles.xml
@@ -19,14 +19,15 @@
 
 <resources>
 
-    <!-- Workspace -->
-    <style name="SearchButton"></style>
-
-    <style name="DropTargetButtonContainer">
-        <item name="android:layout_width">0dp</item>
-        <item name="android:layout_height">match_parent</item>
+    <style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:colorBackgroundCacheHint">@null</item>
+        <item name="android:windowShowWallpaper">true</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowActionModeOverlay">true</item>
     </style>
 
+    <!-- Workspace -->
     <style name="DropTargetButton" parent="DropTargetButtonBase">
         <item name="android:paddingLeft">60dp</item>
         <item name="android:paddingRight">60dp</item>
diff --git a/res/values-sw768dp-port/dimens.xml b/res/values-sw768dp-port/dimens.xml
deleted file mode 100644
index 4df1725..0000000
--- a/res/values-sw768dp-port/dimens.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 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.
--->
-
-<resources>
-<!-- Container -->
-    <dimen name="container_max_width">736dp</dimen>
-
-</resources>
\ No newline at end of file
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index ef1fee4..7db0ae4 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"குறுக்குவழி இல்லை"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"விட்ஜெட்டைத் தேர்வுசெய்ய தொட்டுப் பிடிக்கவும்."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"விட்ஜெட்டைத் தேர்ந்தெடுக்க இருமுறை தட்டிப் பிடிக்கவும் அல்லது தனிப்பயன் செயல்களைப் பயன்படுத்தவும்."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"பயன்பாடுகளில் தேடுக…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d அகலத்திற்கு %2$d உயரம்"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"பயன்பாடுகளில் தேடுக"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"பயன்பாடுகளை ஏற்றுகிறது..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>க்குச் செல்லவும்"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"கூடுதல் பயன்பாடுகளைத் தேடு"</string>
     <string name="out_of_space" msgid="4691004494942118364">"முகப்புத் திரையில் இடமில்லை."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"பிடித்தவை ட்ரேயில் இடமில்லை"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"பயன்பாடுகள்"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"பயன்பாடுகளின் பட்டியல்"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"முகப்பு"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"அகற்று"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"நிறுவல் நீக்கு"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"பயன்பாட்டுத் தகவல்"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"அகற்று"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"நிறுவல் நீக்கு"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"பயன்பாட்டுத் தகவல்"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"குறுக்குவழிகளை நிறுவுதல்"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் பயன்பாட்டை அனுமதிக்கிறது."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"முகப்பின் அமைப்பு மற்றும் குறுக்குவழிகளைப் படித்தல்"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"அமைவு"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"இது அமைப்பு பயன்பாடு என்பதால் நிறுவல் நீக்கம் செய்ய முடியாது."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"பெயரிடப்படாத கோப்புறை"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> முடக்கப்பட்டது"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"பக்கம் %1$d / %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"முகப்புத் திரை %1$d of %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"புதிய முகப்புத் திரை பக்கம்"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"நல்வரவு"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"பயன்பாட்டின் ஐகான்களை நகலெடுக்கவும்"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"பழைய முகப்புத் திரைகளிலிருந்து ஐகான்களையும் கோப்புறைகளையும் இறக்குமதி செய்யவா?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"ஐகான்களை நகலெடு"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"புதிதாகத் தொடங்கு"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"வால்பேப்பர்கள், விட்ஜெட்கள் &amp; அமைப்புகள்"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"தனிப்பயனாக்க, பின்னணியைத் தொட்டுப் பிடிக்கவும்"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"புரிந்தது"</string>
     <string name="folder_opened" msgid="94695026776264709">"திறக்கப்பட்டக் கோப்புறை, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"கோப்புறையை மூட, தொடவும்"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"மறுபெயரிட்டதைச் சேமிக்க, தொடவும்"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"கோப்புறையை மூட, தட்டவும்"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"மாற்றிய பெயரைச் சேமிக்க, தட்டவும்"</string>
     <string name="folder_closed" msgid="4100806530910930934">"கோப்புறை மூடப்பட்டது"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"கோப்புறை <xliff:g id="NAME">%1$s</xliff:g> என மறுபெயரிடப்பட்டது"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"வால்பேப்பர்கள்"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"அமைப்பு"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"உங்கள் நிர்வாகி முடக்கியுள்ளார்"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"சுழற்ற அனுமதி"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"மேலோட்டப் பார்வை"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"முகப்புத் திரை சுழற்சியை அனுமதி"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"மொபைலைச் சுழற்றும் போது"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"தற்போதைய திரை அமைப்பு சுழற்றுவதை அனுமதிக்கவில்லை"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"தெரியாதது"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"அகற்று"</string>
     <string name="abandoned_search" msgid="891119232568284442">"தேடு"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"அகலத்தைக் குறை"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"உயரத்தைக் குறை"</string>
     <string name="widget_resized" msgid="9130327887929620">"அகலம் <xliff:g id="NUMBER_0">%1$s</xliff:g> மற்றும் உயரம் <xliff:g id="NUMBER_1">%2$s</xliff:g>க்கு விட்ஜெட் அளவு மாற்றப்பட்டது"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"குறுக்குவழிகள்"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g>க்கான <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> குறுக்குவழிகள்"</string>
 </resources>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index c3a8cd4..4e8b86f 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"సత్వరమార్గం అందుబాటులో లేదు"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"విడ్జెట్‌ను ఎంచుకోవడానికి తాకి &amp; నొక్కి పెట్టండి."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"విడ్జెట్‌ను ఎంచుకోవడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కి, ఉంచండి."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"అనువర్తనాల్లో శోధించండి…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d వెడల్పు X %2$d ఎత్తు"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"అనువర్తనాలను శోధించండి"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"అనువర్తనాలను లోడ్ చేస్తోంది…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"కి సరిపోలే అనువర్తనాలేవీ కనుగొనబడలేదు"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>కి వెళ్లు"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"మరిన్ని అనువర్తనాల కోసం శోధించు"</string>
     <string name="out_of_space" msgid="4691004494942118364">"ఈ హోమ్ స్క్రీన్‌లో ఖాళీ లేదు."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ఇష్టమైనవి ట్రేలో ఖాళీ లేదు"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"అనువర్తనాలు"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"అనువర్తనాల జాబితా"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"హోమ్"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"తీసివేయి"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"అన్ఇన్‌స్టాల్ చేయి"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"అనువర్తన సమాచారం"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"తీసివేయి"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"అన్ఇన్‌స్టాల్ చేయి"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"అనువర్తన సమాచారం"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"సత్వరమార్గాలను ఇన్‌స్టాల్ చేయడం"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను జోడించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"హోమ్ సెట్టింగ్‌లు మరియు సత్వరమార్గాలను చదవడం"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"సెటప్ చేయి"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"ఇది సిస్టమ్ అనువర్తనం మరియు దీన్ని అన్‌ఇన్‌స్టాల్ చేయడం సాధ్యపడదు."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"పేరు లేని ఫోల్డర్"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> నిలిపివేయబడింది"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$dలో %1$dవ పేజీ"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dలో %1$dవ హోమ్ స్క్రీన్"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"కొత్త హోమ్ స్క్రీన్ పేజీ"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"స్వాగతం"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"మీ అనువర్తన చిహ్నాలను కాపీ చేయండి"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"మీ పాత హోమ్ స్క్రీన్‌ల నుండి చిహ్నాలు మరియు ఫోల్డర్‌లను దిగుమతి చేయాలా?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"చిహ్నాలను కాపీ చేయి"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"తాజాగా ప్రారంభించు"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"వాల్‌పేపర్‌లు, విడ్జెట్‌లు &amp; సెట్టింగ్‌లు"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"అనుకూలీకరించడానికి నేపథ్యాన్ని నొక్కి &amp; ఉంచండి"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"అర్థమైంది"</string>
     <string name="folder_opened" msgid="94695026776264709">"ఫోల్డర్ తెరవబడింది, <xliff:g id="WIDTH">%1$d</xliff:g> X <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"ఫోల్డర్‌ను మూసివేయడానికి తాకండి"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"పేరు మార్పును సేవ్ చేయడానికి తాకండి"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"ఫోల్డర్‌ను మూసివేయడానికి నొక్కండి"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"పేరు మార్పును సేవ్ చేయడానికి నొక్కండి"</string>
     <string name="folder_closed" msgid="4100806530910930934">"ఫోల్డర్ మూసివేయబడింది"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"ఫోల్డర్ పేరు <xliff:g id="NAME">%1$s</xliff:g>గా మార్చబడింది"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"వాల్‌పేపర్‌లు"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"సెట్టింగ్‌లు"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"మీ నిర్వాహకులు నిలిపివేసారు"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"భ్రమణాన్ని అనుమతించండి"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"స్థూలదృష్టి"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"హోమ్ స్క్రీన్ భ్రమణాన్ని అనుమతించండి"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"ఫోన్‌‌ను తిప్పినప్పుడు"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"ప్రస్తుత డిస్‌ప్లే సెట్టింగ్ భ్రమణాన్ని అనుమతించలేదు"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"తెలియదు"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"తీసివేయి"</string>
     <string name="abandoned_search" msgid="891119232568284442">"శోధించు"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"వెడల్పును తగ్గించు"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"ఎత్తును తగ్గించు"</string>
     <string name="widget_resized" msgid="9130327887929620">"విడ్జెట్ పరిమాణం వెడల్పు <xliff:g id="NUMBER_0">%1$s</xliff:g>కి, ఎత్తు <xliff:g id="NUMBER_1">%2$s</xliff:g>కి మార్చబడింది"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"సత్వరమార్గాలు"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> కోసం <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> సత్వరమార్గాలు"</string>
 </resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index ead28d6..90dec72 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"ทางลัดไม่พร้อมใช้งาน"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"แตะค้างเพื่อรับวิดเจ็ต"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"แตะ 2 ครั้งค้างไว้เพื่อเลือกวิดเจ็ตหรือใช้การกระทำที่กำหนดเอง"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ค้นหาแอป…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"กว้าง %1$d x สูง %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ค้นหาแอป"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"กำลังโหลดแอป…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"ไม่พบแอปที่ตรงกับ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"ไปที่ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"ค้นหาแอปเพิ่มเติม"</string>
     <string name="out_of_space" msgid="4691004494942118364">"ไม่มีที่ว่างในหน้าจอหลักนี้"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ไม่มีพื้นที่เหลือในถาดรายการโปรด"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"แอป"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"รายชื่อแอป"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"หน้าแรก"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"ลบ"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"ถอนการติดตั้ง"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"ข้อมูลแอป"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"นำออก"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"ถอนการติดตั้ง"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"ข้อมูลแอป"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"ติดตั้งทางลัด"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"อนุญาตให้แอปเพิ่มทางลัดโดยไม่ต้องให้ผู้ใช้จัดการ"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"อ่านการตั้งค่าและทางลัดหน้าแรกแล้ว"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"ตั้งค่า"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"นี่เป็นแอประบบและไม่สามารถถอนการติดตั้งได้"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"โฟลเดอร์ที่ไม่มีชื่อ"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"ปิดใช้ <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"หน้า %1$d จาก %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"หน้าจอหลัก %1$d จาก %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"หน้าใหม่ในหน้าจอหลัก"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"ยินดีต้อนรับ"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"คัดลอกไอคอนแอปของคุณ"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"นำเข้าไอคอนและโฟลเดอร์จากหน้าจอหลักเก่าของคุณ"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"คัดลอกไอคอน"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"เริ่มต้นใหม่"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"วอลเปเปอร์ วิดเจ็ต และการตั้งค่า"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"แตะพื้นหลังค้างไว้เพื่อกำหนดค่า"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"รับทราบ"</string>
     <string name="folder_opened" msgid="94695026776264709">"เปิดโฟลเดอร์ <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"แตะเพื่อปิดโฟลเดอร์"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"แตะเพื่อบันทึกการเปลี่ยนชื่อ"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"แตะเพื่อปิดโฟลเดอร์"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"แตะเพื่อบันทึกการเปลี่ยนชื่อ"</string>
     <string name="folder_closed" msgid="4100806530910930934">"โฟลเดอร์ปิดอยู่"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"เปลี่ยนชื่อโฟลเดอร์เป็น <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"โฟลเดอร์: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"วอลเปเปอร์"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"การตั้งค่า"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ปิดใช้โดยผู้ดูแลระบบ"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"อนุญาตให้ใช้การหมุน"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"ภาพรวม"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"อนุญาตให้หมุนหน้าจอหลัก"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"เมื่อหมุนโทรศัพท์"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"การตั้งค่าการแสดงผลปัจจุบันไม่อนุญาตให้มีการหมุน"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"ไม่รู้จัก"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ลบ"</string>
     <string name="abandoned_search" msgid="891119232568284442">"ค้นหา"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"ลดความกว้าง"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"ลดความสูง"</string>
     <string name="widget_resized" msgid="9130327887929620">"ปรับขนาดของวิดเจ็ตเป็นกว้าง <xliff:g id="NUMBER_0">%1$s</xliff:g> สูง <xliff:g id="NUMBER_1">%2$s</xliff:g> แล้ว"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"ทางลัด"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ทางลัดสำหรับ <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 91e04fb..b944ebc 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Hindi available ang app"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Naka-disable ang na-download na app sa Safe mode"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Naka-disable ang mga widget sa Safe mode"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Hindi available ang shortcut"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Pindutin nang matagal upang kumuha ng widget."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"I-double tap nang matagal upang pumili ng widget o gumamit ng mga custom na pagkilos."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Maghanap ng Mga App…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ang lapad at %2$d ang taas"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Mga App sa Paghahanap"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Nilo-load ang Mga App…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Walang nakitang Mga App na tumutugma sa \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Pumunta sa <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Maghanap ng higit pang mga app"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Wala nang lugar sa Home screen na ito."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Wala nang lugar sa tray ng Mga Paborito"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Listahan ng mga app"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Home"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Alisin"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"I-uninstall"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Impormasyon ng app"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Alisin"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"I-uninstall"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Impormasyon ng app"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"i-install ang mga shortcut"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Pinapayagan ang isang app na magdagdag ng mga shortcut nang walang panghihimasok ng user."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"basahin ang mga setting at shortcut ng Home"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"I-setup"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Isa itong app ng system at hindi maaaring i-uninstall."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Walang Pangalang Folder"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Naka-disable ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Pahina %1$d ng %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d ng %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Bagong page ng home screen"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Maligayang Pagdating"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopyahin ang mga icon ng app"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"I-import ang icon at folder mula sa luma mong Home screen?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPYAHIN ANG MGA ICON"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"MAGSIMULA NANG BAGO"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Mga wallpaper, widget at setting"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Pindutin nang matagal ang background upang i-customize"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"NAKUHA KO"</string>
     <string name="folder_opened" msgid="94695026776264709">"Binuksan ang folder, <xliff:g id="WIDTH">%1$d</xliff:g> by <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Pindutin upang isara ang folder"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Pindutin upang i-save ang pagpapalit ng pangalan"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"I-tap upang isara ang folder"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"I-tap upang i-save ang bagong pangalan"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Nakasara ang folder"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Pinalitan ang pangalan ng folder ng <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Mga Wallpaper"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Mga Setting"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Na-disable ng iyong admin"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Payagan ang pag-rotate"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Pangkalahatang-ideya"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Payagan ang pag-rotate ng Home screen"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Kailan maro-rotate ang telepono"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Hindi pinahihintulutan ng kasalukuyang setting ng Display ang pag-rotate"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Hindi kilala"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Alisin"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Maghanap"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Bawasan ang lapad"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Bawasan ang taas"</string>
     <string name="widget_resized" msgid="9130327887929620">"Na-resize ang widget sa lapad <xliff:g id="NUMBER_0">%1$s</xliff:g> taas <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Mga Shortcut"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> (na) shortcut para sa <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index fc4a69f..e95ee2d 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -26,20 +26,22 @@
     <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>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Güvenli modda widget\'lar devre dışı"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Kısayol kullanılamıyor"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Widget seçmek için dokunun ve basılı tutun."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Bir widget\'ı seçmek veya özel işlemleri kullanmak için iki kez hafifçe dokunun ve basılı tutun."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Uygulamalarda Ara…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"genişlik: %1$d, yükseklik: %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Uygulamalarda Ara"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Uygulamalar Yükleniyor…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ile eşleşen uygulama bulunamadı"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> uygulamasına git"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Başka uygulamalar ara"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Bu Ana ekranda yer kalmadı."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoriler tepsisinde başka yer kalmadı"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Uygulamalar"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Uygulamalar listesi"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Ana ekran"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Kaldır"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Yüklemeyi kaldır"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Uygulama bilgileri"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Kaldır"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Yüklemeyi kaldır"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Uygulama bilgileri"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"kısayolları yükle"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Uygulamaya, kullanıcı müdahalesi olmadan kısayol ekleme izni verir."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"Ana ekran ayarlarını ve kısayollarını oku"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Kurulum"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu bir sistem uygulamasıdır ve yüklemesi kaldırılamaz."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Adsız Klasör"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> devre dışı"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Sayfa %1$d / %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ana ekran %1$d / %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Yeni ana ekran sayfası"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Hoş geldiniz"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Uygulama simgelerini kopyala"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Eski Ana ekranlarınızdaki simgeler ve klasörler içe aktarılsın mı?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"SİMGELERİ KOPYALA"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"VARSAYILANI KULLAN"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Duvar kağıtları, widget\'lar ve ayarlar"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Özelleştirmek için arka plana dokunun ve basılı tutun"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"TAMAM"</string>
     <string name="folder_opened" msgid="94695026776264709">"Klasör açıldı, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Klasörü kapatmak için dokunun"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Yeni adı kaydetmek için dokunun"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Klasörü kapatmak için hafifçe dokunun"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Yeni adın kaydedilmesi için hafifçe dokunun"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Klasör kapatıldı"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Klasörün adı <xliff:g id="NAME">%1$s</xliff:g> olarak değiştirildi"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Duvar Kağıtları"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Ayarlar"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Yöneticiniz tarafından devre dışı bırakıldı"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Döndürmeye izin ver"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Genel bakış"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Ana ekranı döndürmeye izin ver"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon döndürüldüğünde"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Mevcut Ekran ayarı, döndürmeye izin vermiyor"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Bilinmiyor"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Kaldır"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Ara"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Genişliği azalt"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Yüksekliği azalt"</string>
     <string name="widget_resized" msgid="9130327887929620">"Widget, <xliff:g id="NUMBER_0">%1$s</xliff:g> genişlik ve <xliff:g id="NUMBER_1">%2$s</xliff:g> yükseklik değerine yeniden boyutlandırıldı"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Kısayollar"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> için <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> kısayol"</string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 9607099..1f73f04 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"Ярлик недоступний"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Натисніть і утримуйте, щоб вибрати віджет."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Двічі натисніть і утримуйте, щоб вибрати віджет, або виконайте іншу дію."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Пошук додатків…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина – %1$d, висота – %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Пошук додатків"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Завантаження додатків…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Немає додатків для запиту \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Перейти в додаток <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Шукати ще додатки"</string>
     <string name="out_of_space" msgid="4691004494942118364">"На цьому головному екрані більше немає місця."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"В області \"Вибране\" немає місця"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Додатки"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Список додатків"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Головний екран"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Вилучити"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Видалити"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Про програму"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Видалити"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Видалити"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Про додаток"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"створення ярликів"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Дозволяє програмі самостійно додавати ярлики."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"читати налаштування та ярлики головного екрана"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Налаштування"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Це системна програма, її неможливо видалити."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Папка без назви"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> вимкнено"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Сторінка %1$d з %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Головний екран %1$d з %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Нова сторінка головного екрана"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Вітаємо"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Копіювати значки програм"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Імпортувати значки та папки зі старих головних екранів?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"КОПІЮВАТИ ЗНАЧКИ"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"ПАНЕЛЬ ЗАПУСКУ ЗА УМОВЧАННЯМ"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Фонові малюнки, віджети й налаштування"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Натисніть і втримуйте фон, щоб налаштувати робочу область"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ЗРОЗУМІЛО"</string>
     <string name="folder_opened" msgid="94695026776264709">"Папку відкрито (<xliff:g id="WIDTH">%1$d</xliff:g> х <xliff:g id="HEIGHT">%2$d</xliff:g>)"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Торкніться, щоб закрити папку"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Торкніться, щоб зберегти нову назву"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Торкніться, щоб закрити папку"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Торкніться, щоб зберегти зміни"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Папку закрито"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Папку перейменовано на <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Папка <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Фонові малюнки"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Налаштування"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Вимкнув адміністратор"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Дозволити обертання"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Огляд"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволити обертання головного екрана"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Коли телефон обертається"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Поточні налаштування дисплея не підтримують обертання"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Невідомо"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Видалити"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Шукати"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Зменшити ширину"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Зменшити висоту"</string>
     <string name="widget_resized" msgid="9130327887929620">"Розміри віджета змінено на <xliff:g id="NUMBER_0">%1$s</xliff:g> завширшки та <xliff:g id="NUMBER_1">%2$s</xliff:g> заввишки"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Ярлики"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"Ярликів для додатка <xliff:g id="APP_NAME">%2$s</xliff:g>: <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g>"</string>
 </resources>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index e7ac4b3..e4c637d 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"شارٹ کٹ دستیاب نہیں ہے"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"کوئی ویجیٹ منتخب کرنے کیلئے ٹچ کریں اور پکڑے رہیں۔"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"کوئی ویجٹ منتخب کرنے یا حسب ضرورت کاروائیاں استعمال کرنے کیلئے دو بار تھپتھپائیں اور پکڑے رکھیں۔"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ایپس تلاش کریں…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏%1$d چوڑا اور ‎%2$d اونچا"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ایپس تلاش کریں"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"ایپس لوڈ ہو رہی ہیں…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" سے مماثل کوئی ایپس نہیں ملیں"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> پر جائیں"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"مزید ایپس تلاش کریں"</string>
     <string name="out_of_space" msgid="4691004494942118364">"اس ہوم اسکرین پر مزید کوئی گنجائش نہیں ہے۔"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"پسندیدہ ٹرے میں مزید کوئی گنجائش نہیں ہے"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"ایپس"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"ایپس کی فہرست"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"ہوم"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"ہٹائیں"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"اَن انسٹال کریں"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"ایپ کی معلومات"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"ہٹائیں"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"اَن انسٹال کریں"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"ایپ کی معلومات"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"شارٹ کٹس انسٹال کریں"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"کسی ایپ کو صارف کی مداخلت کے بغیر شارٹ کٹس شامل کرنے کی اجازت دیتا ہے۔"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"ہوم ترتیبات اور شارٹ کٹس کو پڑھیں"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"ترتیب دیں"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"یہ ایک سسٹم ایپ ہے اور اسے اَن انسٹال نہیں کیا جا سکتا ہے۔"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"بلا نام فولڈر"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> غیر فعال ہے"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"‏صفحہ ‎%1$d از ‎%2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"‏ہوم اسکرین ‎%1$d از ‎%2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"نیا ہوم اسکرین صفحہ"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"خوش آمدید"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"اپنے ایپ آئیکنز کو کاپی کریں"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"اپنی پرانی ہوم اسکرینوں سے آئیکنز اور فولڈرز درآمد کریں؟"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"آئیکنز کاپی کریں"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"نئے سرے سے شروع کریں"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"وال پیپرز، ویجیٹس اور ترتیبات"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"حسب ضرورت بنانے کیلئے پس منظر کو ٹچ کریں اور دبائے رکھیں"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"سمجھ آ گئی"</string>
     <string name="folder_opened" msgid="94695026776264709">"فولڈر کھولا گیا، <xliff:g id="WIDTH">%1$d</xliff:g> × <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"فولڈر بند کرنے کیلئے ٹچ کریں"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"نام کی تبدیلی محفوظ کرنے کیلئے ٹچ کریں"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"فولڈر کو بند کرنے کیلئے تھپتھپائیں"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"نام کی تبدیلی محفوظ کرنے کیلئے تھپتھپائیں"</string>
     <string name="folder_closed" msgid="4100806530910930934">"فولڈر بند ہو گیا"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"فولڈر کا نام تبدیل کر کے <xliff:g id="NAME">%1$s</xliff:g> کر دیا گیا"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"وال پیپرز"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"ترتیبات"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"آپ کے منتظم کی طرف سے غیر فعال کر دیا گیا"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"گردش کی اجازت دیں"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"مجموعی جائزہ"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"ہوم اسکرین گھمانے کی اجازت دیں"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"جب فون گھمایا جاتا ہے"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"موجودہ ڈسپلے ترتیب گھمانے کی اجازت نہیں دیتی"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"نامعلوم"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ہٹائیں"</string>
     <string name="abandoned_search" msgid="891119232568284442">"تلاش کریں"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"چوڑائی کم کریں"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"اونچائی کم کریں"</string>
     <string name="widget_resized" msgid="9130327887929620">"ویجیٹ کے سائز کو چوڑائی <xliff:g id="NUMBER_0">%1$s</xliff:g> اونچائی <xliff:g id="NUMBER_1">%2$s</xliff:g> میں تبدیل کر دیا گیا"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"شارٹ کٹس"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> کیلئے <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> شارٹ کٹس"</string>
 </resources>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index fd71c41..0e541d3 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Ilova mavjud emas"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Yuklab olingan ilova xavfsiz rejimda o‘chirib qo‘yildi"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Xavfsiz rejimda vidjetlar o‘chirib qo‘yilgan"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Tezkor tugmadan foydalanib bo‘lmaydi"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Vidjetni tanlash uchun bosib turing."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ikki marta bosib va bosib turgan holatda vidjetni tanlang yoki maxsus amaldan foydalaning."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Ilovalarni qidirish…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Eni %1$d, bo‘yi %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Ilovalar ichidan qidirish"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Ilovalar yuklanmoqda…"</string>
-    <string name="all_apps_no_search_results" msgid="6332185285860416787">"“<xliff:g id="QUERY">%1$s</xliff:g>” bilan mos hech qanday ilova topilmadi"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"O‘tish: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_no_search_results" msgid="6332185285860416787">"“<xliff:g id="QUERY">%1$s</xliff:g>” so‘rovi bo‘yicha hech narsa topilmadi"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Boshqa ilovalarni qidirish"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Uy ekranida bitta ham xona yo‘q."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ajratilganlarda birorta ham xona yo‘q"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Ilovalar"</string>
-    <string name="all_apps_home_button_label" msgid="252062713717058851">"Uy"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Olib tashlash"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"O‘chirib tashlash"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Ilova haqida"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Ilovalar ro‘yxati"</string>
+    <string name="all_apps_home_button_label" msgid="252062713717058851">"Bosh sahifa"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Olib tashlash"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"O‘chirib tashlash"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Ilova haqida"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"yorliqlar yaratish"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"Uy sozlamalari va yorliqlarini o‘qish"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Sozlash"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu tizim ilovasi, shuning uchun o‘chirib bo‘lmaydi."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Nomsiz jild"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi o‘chirib qo‘yildi"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"%2$ddan %1$d ta sahifa"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Uy ekrani %2$ddan %1$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Yangi bosh ekran sahifasi"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Xush kelibsiz"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Ilovangiz nishonchalaridan nusxa olish"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Eski \"Uy\" ekranlaringizdan jildlar va nishonchalar import qilinsinmi?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"NISHONCHALARNI NUSXALASH"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"YANGIDAN BOSHLASH"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Orqa fon rasmlari, vidjet va sozlamalar"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Orqa fonni moslashtirish uchun uni bosing va ushlab turing"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
     <string name="folder_opened" msgid="94695026776264709">"Jild ochildi, <xliff:g id="WIDTH">%1$d</xliff:g> ga <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Jildni yopish uchun bosing"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"O‘zgartirilgan nomni saqlash uchun bosing"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Jildni yopish uchun ustiga bosing"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"O‘zgarishni saqlash uchun ustiga bosing"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Jild yopildi"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Jild nomi <xliff:g id="NAME">%1$s</xliff:g>ga o‘zgartirildi"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Jild: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fon rasmlari"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Sozlamalar"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator tomonidan o‘chirilgan"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Aylanishga ruxsat berish"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Umumiy ko‘rinish"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Asosiy ekranni aylantirishga ruxsat berish"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Telefon burilganda"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Ekran sozlamalariga ko‘ra uni aylantirib bo‘lmaydi"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Noma’lum"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"O‘chirish"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Qidirish"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Enini kichraytirish"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Bo‘yini kichraytirish"</string>
     <string name="widget_resized" msgid="9130327887929620">"Vidjetning eni <xliff:g id="NUMBER_0">%1$s</xliff:g>, bo‘yi <xliff:g id="NUMBER_1">%2$s</xliff:g> qilib o‘zgartirildi"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Tezkor tugmalar"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g> ilovasi uchun <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ta tezkor tugma"</string>
 </resources>
diff --git a/res/values-sw340dp-port/styles.xml b/res/values-v19/styles.xml
similarity index 64%
rename from res/values-sw340dp-port/styles.xml
rename to res/values-v19/styles.xml
index 8ac3b5e..cfc7c0f 100644
--- a/res/values-sw340dp-port/styles.xml
+++ b/res/values-v19/styles.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-* Copyright (C) 2011 The Android Open Source Project
+* Copyright (C) 2016 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.
@@ -16,12 +16,11 @@
 * limitations under the License.
 */
 -->
-
 <resources>
-<!-- Workspace -->
-    <style name="SearchButton">
-        <item name="android:layout_gravity">center_vertical</item>
-        <item name="android:paddingTop">@dimen/toolbar_button_vertical_padding</item>
-        <item name="android:paddingBottom">@dimen/toolbar_button_vertical_padding</item>
+
+    <style name="LauncherTheme" parent="@style/BaseLauncherTheme">
+        <item name="android:windowTranslucentStatus">true</item>
+        <item name="android:windowTranslucentNavigation">true</item>
     </style>
-</resources>
+
+</resources>
\ No newline at end of file
diff --git a/res/values-v21/styles.xml b/res/values-v21/styles.xml
new file mode 100644
index 0000000..8d3de01
--- /dev/null
+++ b/res/values-v21/styles.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2016 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.
+*/
+-->
+<resources>
+
+    <style name="LauncherTheme" parent="@style/BaseLauncherTheme">
+        <item name="android:windowTranslucentStatus">false</item>
+        <item name="android:windowTranslucentNavigation">false</item>
+        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+        <item name="android:statusBarColor">#00000000</item>
+        <item name="android:navigationBarColor">#00000000</item>
+    </style>
+</resources>
diff --git a/res/values-sw340dp-port/styles.xml b/res/values-v25/styles.xml
similarity index 64%
copy from res/values-sw340dp-port/styles.xml
copy to res/values-v25/styles.xml
index 8ac3b5e..ed670a9 100644
--- a/res/values-sw340dp-port/styles.xml
+++ b/res/values-v25/styles.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
 /*
-* Copyright (C) 2011 The Android Open Source Project
+* Copyright (C) 2016 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.
@@ -16,12 +16,9 @@
 * limitations under the License.
 */
 -->
-
 <resources>
-<!-- Workspace -->
-    <style name="SearchButton">
-        <item name="android:layout_gravity">center_vertical</item>
-        <item name="android:paddingTop">@dimen/toolbar_button_vertical_padding</item>
-        <item name="android:paddingBottom">@dimen/toolbar_button_vertical_padding</item>
+    <!-- Theme for the widget container. -->
+    <style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
+        <item name="colorSecondary">?android:attr/colorSecondary</item>
     </style>
 </resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 40947bd..2bf5ad6 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Ứng dụng không có sẵn"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Ứng dụng đã tải xuống bị tắt ở chế độ An toàn"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Tiện ích con bị vô hiệu hóa ở chế độ an toàn"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Lối tắt không khả dụng"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Chạm và giữ để chọn tiện ích con."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Nhấn đúp và giữ để chọn tiện ích hoặc sử dụng tác vụ tùy chỉnh."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Tìm kiếm ứng dụng..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Rộng %1$d x cao %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Tìm kiếm ứng dụng"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Đang tải ứng dụng..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Không tìm thấy ứng dụng nào phù hợp với \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Chuyển tới <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Tìm kiếm thêm ứng dụng"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Không còn chỗ trên Màn hình chính này."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Không còn chỗ trong khay Mục yêu thích"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Ứng dụng"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Danh sách ứng dụng"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Màn hình chính"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Xóa"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Gỡ cài đặt"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Thông tin ứng dụng"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Xóa"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Gỡ cài đặt"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Thông tin ứng dụng"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"cài đặt lối tắt"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Cho phép ứng dụng thêm lối tắt mà không cần sự can thiệp của người dùng."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"đọc cài đặt và lối tắt trên Màn hình chính"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Thiết lập"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Đây là ứng dụng hệ thống và không thể gỡ cài đặt."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Thư mục chưa đặt tên"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Đã vô hiệu hóa <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Trang %1$d / %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Màn hình chính %1$d / %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Trang màn hình chính mới"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Chào mừng"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Sao chép biểu tượng ứng dụng của bạn"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Nhập biểu tượng và thư mục từ Màn hình chính cũ của bạn?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"BIỂU TƯỢNG SAO CHÉP"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"BẮT ĐẦU LÀM MỚI"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Hình nền, tiện ích và cài đặt"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Chạm và giữ nền để tùy chỉnh"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
     <string name="folder_opened" msgid="94695026776264709">"Đã mở thư mục, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Chạm để đóng thư mục"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Chạm để lưu tên mới"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Nhấn để đóng thư mục"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Nhấn để lưu đổi tên"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Đã đóng thư mục"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Đã đổi tên thư mục thành <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Hình nền"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Cài đặt"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Bị tắt bởi quản trị viên của bạn"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Cho phép xoay"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Tổng quan"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Cho phép xoay Màn hình chính"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Khi xoay điện thoại"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Cài đặt Hiển thị hiện tại không cho phép xoay"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Không xác định"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Xóa"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Tìm kiếm"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Giảm chiều rộng"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Giảm chiều cao"</string>
     <string name="widget_resized" msgid="9130327887929620">"Đã đổi kích thước tiện ích thành chiều rộng <xliff:g id="NUMBER_0">%1$s</xliff:g> chiều cao <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Lối tắt"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> phím tắt cho <xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index e333ecb..e3ed354 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"无法使用快捷方式"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"触摸并按住小部件即可选择。"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"点按两次并按住小部件即可选择小部件,您也可以使用自定义操作。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"搜索应用…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"宽 %1$d,高 %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"搜索应用"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"正在加载应用…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"未找到与“<xliff:g id="QUERY">%1$s</xliff:g>”相符的应用"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"转到 <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"搜索更多应用"</string>
     <string name="out_of_space" msgid="4691004494942118364">"此主屏幕上已没有空间。"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"收藏栏已满"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"应用"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"应用列表"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"主屏幕"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"删除"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"卸载"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"应用信息"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"移除"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"卸载"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"应用信息"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"安装快捷方式"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"允许应用自行添加快捷方式。"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"读取主屏幕设置和快捷方式"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"设置"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"这是系统应用,无法卸载。"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"未命名文件夹"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"已停用<xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"第%1$d页,共%2$d页"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"主屏幕:第%1$d屏,共%2$d屏"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"主屏幕新页面"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"欢迎使用"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"复制应用图标"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"要导入旧的主屏幕中的图标和文件夹吗?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"复制图标"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"使用全新配置"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"壁纸、小部件和设置"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"触摸并按住背景,即可进行个性化设置"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"知道了"</string>
     <string name="folder_opened" msgid="94695026776264709">"文件夹已打开,大小为<xliff:g id="WIDTH">%1$d</xliff:g>×<xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"触摸可关闭文件夹"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"触摸可保存新名称"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"点按可关闭文件夹"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"点按可保存新名称"</string>
     <string name="folder_closed" msgid="4100806530910930934">"文件夹已关闭"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"已将文件夹重命名为“<xliff:g id="NAME">%1$s</xliff:g>”"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"壁纸"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"设置"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已被您的管理员停用"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"允许旋转"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"概览"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"允许旋转主屏幕"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"手机旋转时"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"当前的显示设置不允许旋转设备"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"未知"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
     <string name="abandoned_search" msgid="891119232568284442">"搜索"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"减小宽度"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"减小高度"</string>
     <string name="widget_resized" msgid="9130327887929620">"小部件尺寸已调整为:宽度 <xliff:g id="NUMBER_0">%1$s</xliff:g>,高度 <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"快捷方式"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="APP_NAME">%2$s</xliff:g>有 <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> 个快捷方式"</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index b65630e..ad0f99a 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"沒有可用的捷徑"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"輕觸並按住小工具即可選取。"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"連扲兩下,然後扲住,就可以新增小工具,或者執行自訂操作。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"搜尋應用程式…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d 闊,%2$d 高"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"搜尋應用程式"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"正在載入應用程式…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"無法找到與「<xliff:g id="QUERY">%1$s</xliff:g>」相符的應用程式"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"前往 <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"搜尋更多應用程式"</string>
     <string name="out_of_space" msgid="4691004494942118364">"主畫面已無空間。"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"我的收藏寄存區沒有足夠空間"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"應用程式"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"應用程式清單"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"主畫面"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"移除"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"解除安裝"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"應用程式資料"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"移除"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"解除安裝"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"應用程式資料"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式無需使用者許可也可新增捷徑。"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"讀取主畫面的設定和捷徑"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"設定"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"這是系統應用程式,無法將其解除安裝。"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"未命名的資料夾"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」已停用"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"第 %1$d 頁,共 %2$d 頁"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"主畫面 %1$d,共 %2$d 個"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"新主畫面頁面"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"歡迎"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"複製您的應用程式圖示"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"要從舊主畫面匯入圖示和資料夾嗎?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"複製圖示"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"重新開始"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"桌布、小工具和設定"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"輕觸並按住背景即可自訂"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"知道了"</string>
     <string name="folder_opened" msgid="94695026776264709">"資料夾已開啟 (<xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>)"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"輕觸即可關閉資料夾"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"輕觸即可儲存新改的名稱"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"輕按即可關閉資料夾"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"輕按即可儲存新名稱"</string>
     <string name="folder_closed" msgid="4100806530910930934">"已關閉資料夾"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"資料夾已重新命名為「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"設定"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由您的管理員停用"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"允許旋轉"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"概覽"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"允許主畫面旋轉"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"當手機旋轉時"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"「目前顯示屏」設定不允許旋轉"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
     <string name="abandoned_search" msgid="891119232568284442">"搜尋"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"減少闊度"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"減少高度"</string>
     <string name="widget_resized" msgid="9130327887929620">"已調整小工具的大小至闊 <xliff:g id="NUMBER_0">%1$s</xliff:g> 高 <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"捷徑"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"「<xliff:g id="APP_NAME">%2$s</xliff:g>」嘅 <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> 個捷徑"</string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 1c954fd..24f391d 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -26,20 +26,22 @@
     <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="shortcut_not_available" msgid="2536503539825726397">"目前無法使用捷徑"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"輕觸並按住小工具即可選取。"</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"輕觸兩下並按住小工具即可選取,您也可以使用自訂動作。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"搜尋應用程式…"</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"寬度為 %1$d,高度為 %2$d"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"搜尋應用程式"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"正在載入應用程式…"</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"找不到符合「<xliff:g id="QUERY">%1$s</xliff:g>」的應用程式"</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"前往 <xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"搜尋更多應用程式"</string>
     <string name="out_of_space" msgid="4691004494942118364">"這個主螢幕已無空間。"</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"「我的最愛」匣已無可用空間"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"應用程式"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"應用程式清單"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"主螢幕"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"移除"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"解除安裝"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"應用程式資訊"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"移除"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"解除安裝"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"應用程式資訊"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式自動新增捷徑。"</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"讀取主螢幕的設定和捷徑"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"設定"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"這是系統應用程式,不可解除安裝。"</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"未命名的資料夾"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"已停用 <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"第 %1$d 頁,共 %2$d 頁"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"主螢幕:第 %1$d 頁,共 %2$d 頁"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"新的主畫面頁面"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"歡迎使用"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"複製您的應用程式圖示"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"要從舊的主螢幕匯入圖示和資料夾嗎?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"複製圖示"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"重新開始"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"桌布、小工具和設定"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"輕觸並按住背景即可自訂"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"知道了"</string>
     <string name="folder_opened" msgid="94695026776264709">"資料夾已開啟 (<xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>)"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"輕觸即可關閉資料夾"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"輕觸即可儲存新名稱"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"輕觸即可關閉資料夾"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"輕觸即可儲存新名稱"</string>
     <string name="folder_closed" msgid="4100806530910930934">"資料夾已關閉"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"已將資料夾重新命名為「<xliff:g id="NAME">%1$s</xliff:g>」"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"設定"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由您的管理員停用"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"允許輪播"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"總覽"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"允許旋轉主螢幕"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"當手機旋轉時"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"目前的顯示設定不允許旋轉畫面"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
     <string name="abandoned_search" msgid="891119232568284442">"搜尋"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"減少寬度"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"減少高度"</string>
     <string name="widget_resized" msgid="9130327887929620">"已將小工具的寬度和高度分別調整為 <xliff:g id="NUMBER_0">%1$s</xliff:g> 和 <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"捷徑"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"「<xliff:g id="APP_NAME">%2$s</xliff:g>」有 <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> 個捷徑"</string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index af80b6c..10fbef4 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -26,20 +26,22 @@
     <string name="activity_not_available" msgid="7456344436509528827">"Uhlelo lokusebenza alutholakali"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"Uhlelo lokusebenza olulandiwe lukhutshaziwe kumodi ephephile"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"Amawijethi akhutshaziwe kwimodi yokuphepha"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"Isinqamuleli asitholakali"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"Thinta uphinde ubambe ukuze uphakamise iwijethi."</string>
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Thepha kabili bese uyabamba ukuze uthathe iwijethi noma sebenzisa izenzo ezingokwezifiso."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
-    <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Sesha izinhlelo zokusebenza..."</string>
+    <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ububanzi ngokungu-%2$d ukuya phezulu"</string>
+    <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Sesha Izinhlelo Zokusebenza"</string>
     <string name="all_apps_loading_message" msgid="7557140873644765180">"Ilayisha izinhlelo zokusebenza..."</string>
     <string name="all_apps_no_search_results" msgid="6332185285860416787">"Azikho izinhlelo zokusebenza ezitholakele ezifana ne-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
-    <string name="all_apps_search_market_message" msgid="5470761048755751471">"Hamba ku-<xliff:g id="QUERY">%1$s</xliff:g>"</string>
+    <string name="all_apps_search_market_message" msgid="1366263386197059176">"Sesha izinhlelo zokusebenza eziningi"</string>
     <string name="out_of_space" msgid="4691004494942118364">"Asisekho isikhala kulesi sikrini Sasekhaya."</string>
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Asisekho isikhala kwitreyi lezintandokazi"</string>
-    <string name="all_apps_button_label" msgid="9110807029020582876">"Izinhlelo zokusebenza"</string>
+    <string name="all_apps_button_label" msgid="8130441508702294465">"Uhlu lwezinhlelo zokusebenza"</string>
     <string name="all_apps_home_button_label" msgid="252062713717058851">"Ikhaya"</string>
-    <string name="delete_target_label" msgid="1822697352535677073">"Susa"</string>
-    <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Khipha"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Ulwazi lohlelo lokusebenza"</string>
+    <string name="remove_drop_target_label" msgid="7812859488053230776">"Susa"</string>
+    <string name="uninstall_drop_target_label" msgid="4722034217958379417">"Khipha"</string>
+    <string name="app_info_drop_target_label" msgid="692894985365717661">"Ulwazi lohlelo lokusebenza"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"faka izinqamuleli"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ivumela uhlelo lokusebenza ukufaka izinqamuleli ngaphandle kokungenelela komsebenzisi."</string>
     <string name="permlab_read_settings" msgid="1941457408239617576">"funda izilungiselelo zokuthi Ikhaya nezinqamuleli"</string>
@@ -51,20 +53,13 @@
     <string name="gadget_setup_text" msgid="8274003207686040488">"Ukumisa"</string>
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Lolu uhlelo lokusebenza lwesistimu futhi alikwazi ukukhishwa."</string>
     <string name="folder_hint_text" msgid="6617836969016293992">"Ifolda engenagama"</string>
+    <string name="disabled_app_label" msgid="6673129024321402780">"Kukhutshaziwe <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Ikhasi elingu-%1$d kwangu-%2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Isikrini sasekhaya esingu-%1$d se-%2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Ikhasi elisha lesikrini sasekhaya"</string>
-    <string name="first_run_cling_title" msgid="2459738000155917941">"Siyakwamukela"</string>
-    <string name="migration_cling_title" msgid="9181776667882933767">"Kopisha izithonjana zakho zohlelo lokusebenza"</string>
-    <string name="migration_cling_description" msgid="2752413805582227644">"Ngenisa izithonjana namafolda kusukela kuzikrini zakho ezindala zasekhaya?"</string>
-    <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPISHA IZITHONJANA"</string>
-    <string name="migration_cling_use_default" msgid="2626475813981258626">"QALISA KABUSHA"</string>
-    <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Izithombe zangemuva, amawijethi, nezilungiselelo"</string>
-    <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Thinta uphinde ubambe ingemuva ukuze wenze ngokwezifiso"</string>
-    <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"NGIYITHOLILE"</string>
     <string name="folder_opened" msgid="94695026776264709">"Ifolda ivuliwe, <xliff:g id="WIDTH">%1$d</xliff:g> nge-<xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
-    <string name="folder_tap_to_close" msgid="1884479294466410023">"Thinta ukuze uvale ifolda"</string>
-    <string name="folder_tap_to_rename" msgid="9191075570492871147">"Thinta ukuze ulondoloze ukuqamba kabusha"</string>
+    <string name="folder_tap_to_close" msgid="4625795376335528256">"Thepa ukuze uvale ifolda"</string>
+    <string name="folder_tap_to_rename" msgid="4017685068016979677">"Thepha ukuze ulondoloze ukuqamba kabusha"</string>
     <string name="folder_closed" msgid="4100806530910930934">"Ifolda ivaliwe"</string>
     <string name="folder_renamed" msgid="1794088362165669656">"Ifolda iqanjwe kabusha ngo-<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="folder_name_format" msgid="6629239338071103179">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -72,7 +67,10 @@
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Izithombe zangemuva"</string>
     <string name="settings_button_text" msgid="8119458837558863227">"Izilungiselelo"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Kukhutshazwe umlawuli wakho"</string>
-    <string name="allow_rotation_title" msgid="2118706734511831751">"Vumela ukuphenduka"</string>
+    <string name="accessibility_action_overview" msgid="6257665857640347026">"Ukubuka konke"</string>
+    <string name="allow_rotation_title" msgid="7728578836261442095">"Vumela ukuphendukiswa kwesikrini sasekhaya"</string>
+    <string name="allow_rotation_desc" msgid="8662546029078692509">"Uma ifoni iphendukiswa"</string>
+    <string name="allow_rotation_blocked_desc" msgid="3212602545192996253">"Isilungiselelo sesiboniso samanje asivumeli ukuzungezisa"</string>
     <string name="package_state_unknown" msgid="7592128424511031410">"Akwaziwa"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Susa"</string>
     <string name="abandoned_search" msgid="891119232568284442">"Sesha"</string>
@@ -104,4 +102,6 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"Nciphisa ububanzi"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"Nciphisa ubude"</string>
     <string name="widget_resized" msgid="9130327887929620">"Iwijethi inikezwe usayizi omusha ngobubanzi obungu-<xliff:g id="NUMBER_0">%1$s</xliff:g> ubude obungu-<xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"Izinqamuleli"</string>
+    <string name="shortcuts_menu_description" msgid="406159963824238648">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> izinqamuleli ze-<xliff:g id="APP_NAME">%2$s</xliff:g>"</string>
 </resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index c5be9f2..3423835 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -25,60 +25,17 @@
         <attr name="iconDisplay" format="integer">
             <enum name="workspace" value="0" />
             <enum name="all_apps" value="1" />
-            <enum name="widget_section" value="2" />
+            <enum name="folder" value="2" />
+            <enum name="widget_section" value="3" />
         </attr>
         <attr name="deferShadowGeneration" format="boolean" />
         <attr name="customShadows" format="boolean" />
-    </declare-styleable>
-
-    <!-- Page Indicator specific attributes. -->
-    <declare-styleable name="PageIndicator">
-        <attr name="windowSize" format="integer"  />
-    </declare-styleable>
-
-    <!-- Workspace specific attributes. These attributes are used to customize
-         the workspace in XML files. -->
-    <declare-styleable name="Workspace">
-        <!-- The first screen the workspace should display. -->
-        <attr name="defaultScreen" format="integer"  />
-        <!-- The number of horizontal cells in the CellLayout -->
-        <attr name="cellCountX" format="integer"  />
-        <!-- The number of vertical cells in the CellLayout -->
-        <attr name="cellCountY" format="integer"  />
-    </declare-styleable>
-    
-    <!-- Hotseat specific attributes. These attributes are used to customize
-         the hotseat in XML files. -->
-    <declare-styleable name="Hotseat">
-        <!-- The number of horizontal cells in the CellLayout -->
-        <attr name="cellCountX" />
-        <!-- The number of vertical cells in the CellLayout -->
-        <attr name="cellCountY" />
-    </declare-styleable>
-
-    <!-- CellLayout specific attributes. These attributes are used to customize
-         a CellLayout view in XML files. -->
-    <declare-styleable name="CellLayout">
-        <!-- The width of a single cell -->
-        <attr name="cellWidth" format="dimension"  />
-        <!-- The height of a single cell -->
-        <attr name="cellHeight" format="dimension"  />
-        <!-- An override for the width and height gap to allow users to specify
-             a specific size for the page using spacing instead of resolving the
-             spacing from the width of the page -->
-        <attr name="widthGap" format="dimension" />
-        <attr name="heightGap" format="dimension" />
-        <!-- The max gap size for each dimension -->
-        <attr name="maxGap" format="dimension" />
+        <attr name="centerVertically" format="boolean" />
     </declare-styleable>
 
     <!-- PagedView specific attributes. These attributes are used to customize
          a PagedView view in XML files. -->
     <declare-styleable name="PagedView">
-        <!-- A spacing override for the icons within a page -->
-        <attr name="pageLayoutWidthGap" format="dimension" />
-        <attr name="pageLayoutHeightGap" format="dimension" />
-
         <!-- The page indicator for this workspace -->
         <attr name="pageIndicator" format="reference" />
     </declare-styleable>
@@ -123,4 +80,35 @@
     <declare-styleable name="InsettableFrameLayout_Layout">
         <attr name="layout_ignoreInsets" format="boolean" />
     </declare-styleable>
+
+    <declare-styleable name="ButtonDropTarget">
+        <attr name="hideParentOnDisable" format="boolean" />
+    </declare-styleable>
+
+    <!-- Fallback attr for pre-API 25 support -->
+    <attr name="colorSecondary" format="reference|color" />
+
+    <declare-styleable name="InvariantDeviceProfile">
+        <attr name="name" format="string" />
+        <attr name="minWidthDps" format="float" />
+        <attr name="minHeightDps" format="float" />
+
+        <attr name="numRows" format="integer" />
+        <attr name="numColumns" format="integer" />
+        <!-- numFolderRows & numFolderColumns defaults to numRows & numColumns, if not specified -->
+        <attr name="numFolderRows" format="integer" />
+        <attr name="numFolderColumns" format="integer" />
+        <!-- minAllAppsPredictionColumns defaults to numColumns, if not specified -->
+        <attr name="minAllAppsPredictionColumns" format="integer" />
+        <!-- numHotseatIcons defaults to numColumns, if not specified -->
+        <attr name="numHotseatIcons" format="integer" />
+
+        <attr name="iconSize" format="float" />
+        <attr name="iconTextSize" format="float" />
+        <!-- hotseatIconSize defaults to iconSize, if not specified -->
+        <attr name="hotseatIconSize" format="float" />
+
+        <attr name="defaultLayoutId" format="reference" />
+    </declare-styleable>
+
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 8a7f627..d5ce786 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -4,16 +4,16 @@
 **
 ** Copyright 2008, 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 
+** 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 
+**     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 
+** 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.
 */
 -->
@@ -22,8 +22,6 @@
          over the delete target or the info target -->
     <color name="delete_target_hover_tint">#FFC1C1C1</color>
     <color name="uninstall_target_hover_tint">#FFF0592B</color>
-    <color name="info_target_hover_tint">#FF009688</color>
-    <color name="cling_scrim_background">#80000000</color>
 
     <color name="focused_background">#80c6c5c5</color>
 
@@ -31,23 +29,25 @@
 
     <color name="workspace_edge_effect_color">#FFFFFFFF</color>
     <color name="folder_edge_effect_color">#FF757575</color>
+    <color name="page_indicator_dot_color">#FFDDDDDD</color>
 
     <color name="quantum_panel_text_color">#FF666666</color>
     <color name="quantum_panel_bg_color">#FFF5F5F5</color>
-    <color name="quantum_panel_bg_color_dark">#FF374248</color>
 
     <color name="outline_color">#FFFFFFFF</color>
+    <color name="all_apps_divider_color">#1E000000</color>
+    <color name="all_apps_caret_color">#FFFFFFFF</color>
+    <color name="all_apps_caret_shadow_color">#22000000</color>
+    <color name="all_apps_container_color">#FFF2F2F2</color>
+    <color name="all_apps_navbar_color">#28000000</color>
 
-    <!-- Containers -->
-    <color name="container_fastscroll_thumb_inactive_color">#009688</color>
-    <color name="container_fastscroll_thumb_active_color">#009688</color>
-
-    <!-- All Apps -->
-    <color name="all_apps_grid_section_text_color">#009688</color>
-    <color name="all_apps_search_market_button_focused_bg_color">#DDDDDD</color>
+    <color name="spring_loaded_panel_color">#40FFFFFF</color>
+    <color name="spring_loaded_highlighted_panel_border_color">#FFF</color>
 
     <!-- Widgets view -->
     <color name="widgets_view_section_text_color">#FFFFFF</color>
     <color name="widgets_view_item_text_color">#C4C4C4</color>
-    <color name="widgets_cell_color">#263238</color>
+
+    <!-- Used as a fallback since colorSecondary doesn't exist pre-API 25 -->
+    <color name="fallback_secondary_color">#FF37474F</color>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index c8a610d..a942f02 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -9,31 +9,28 @@
     <bool name="is_large_tablet">false</bool>
     <bool name="allow_rotation">false</bool>
 
-    <!-- Max number of page indicators to show -->
-    <integer name="config_maxNumberOfPageIndicatorsToShow">21</integer>
-
-    <!-- App data backup and restore. To enble backup, register with an android backup service.
-         http://developer.android.com/guide/topics/data/backup.html#BackupKey -->
-    <bool name="enable_backup">false</bool>
+    <!-- A string pointer to the original app name string. This allows derived projects to
+     easily override the app name without providing all translations -->
+    <string name="derived_app_name" translatable="false">@string/app_name</string>
 
 <!-- DragController -->
-    <integer name="config_flingToDeleteMinVelocity">-1500</integer>
     <item type="id" name="drag_event_parity" />
 
 <!-- AllApps & Launcher transitions -->
     <!-- The alpha of the AppsCustomize bg in spring loaded mode -->
-    <integer name="config_workspaceScrimAlpha">55</integer>
+    <integer name="config_workspaceScrimAlpha">30</integer>
     <integer name="config_allAppsTransitionTime">100</integer>
     <integer name="config_overviewTransitionTime">250</integer>
 
     <!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
-    <integer name="config_workspaceSpringLoadShrinkPercentage">80</integer>
+    <integer name="config_workspaceSpringLoadShrinkPercentage">90</integer>
     <!-- Out of 100, the percent to shrink the workspace during overview mode. -->
     <integer name="config_workspaceOverviewShrinkPercentage">70</integer>
 
     <!-- Fade/zoom in/out duration & scale in a Launcher overlay transition.
          Note: This should be less than the config_overlayTransitionTime as they happen together. -->
     <integer name="config_overlayRevealTime">220</integer>
+    <integer name="config_overlaySlideRevealTime">320</integer>
     <integer name="config_overlayTransitionTime">300</integer>
     <integer name="config_overlayItemsAlphaStagger">60</integer>
 
@@ -41,7 +38,8 @@
          is used for internal (baked-in) padding -->
     <integer name="config_allAppsButtonPaddingPercent">17</integer>
 
-    <integer name="config_workspaceDefaultScreen">0</integer>
+    <!-- The duration of the animation from search hint to text entry -->
+    <integer name="config_searchHintAnimationDuration">50</integer>
 
 <!-- Workspace -->
     <!-- The duration (in ms) of the fade animation on the object outlines, used when
@@ -64,6 +62,9 @@
     <!-- The distance at which the animation should take the max duration -->
     <integer name="config_dropAnimMaxDist">800</integer>
 
+    <!-- The duration of the caret animation -->
+    <integer name="config_caretAnimationDuration">200</integer>
+
 <!-- Hotseat -->
     <bool name="hotseat_transpose_layout_with_orientation">true</bool>
 
@@ -71,16 +72,32 @@
          filter the activities shown in the launcher. Can be empty. -->
     <string name="app_filter_class" translatable="false"></string>
 
-    <!-- Name of a packages that com.android.launcher3.action.LAUNCH
-         should be targeting. Can be empty. -->
-    <string-array name="launch_broadcast_targets" translatable="false"></string-array>
+    <!-- List of package names that com.android.launcher3.action.LAUNCH
+     should be targeting. Can be empty. -->
+    <array name="launch_broadcast_targets" translatable="false"></array>
+
+    <!-- Name of an icon provider class. -->
+    <string name="icon_provider_class" translatable="false"></string>
+
+    <!-- Package name of the default wallpaper picker. -->
+    <string name="wallpaper_picker_package" translatable="false"></string>
 
     <!-- View ID to use for QSB widget -->
     <item type="id" name="qsb_widget" />
 
+    <!-- View ID to use for blocked area on the first screen -->
+    <item type="id" name="workspace_blocked_row" />
+
     <!-- View ID used by cell layout to jail its content -->
     <item type="id" name="cell_layout_jail_id" />
 
+<!-- Deep shortcuts -->
+    <integer name="config_deepShortcutOpenDuration">220</integer>
+    <integer name="config_deepShortcutArrowOpenDuration">80</integer>
+    <integer name="config_deepShortcutOpenStagger">40</integer>
+    <integer name="config_deepShortcutCloseDuration">150</integer>
+    <integer name="config_deepShortcutCloseStagger">20</integer>
+
 <!-- Accessibility actions -->
     <item type="id" name="action_remove" />
     <item type="id" name="action_uninstall" />
@@ -91,4 +108,5 @@
     <item type="id" name="action_move_screen_backwards" />
     <item type="id" name="action_move_screen_forwards" />
     <item type="id" name="action_resize" />
+    <item type="id" name="action_deep_shortcuts" />
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index af4ae5e..eff9d21 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -4,9 +4,9 @@
      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.
@@ -15,55 +15,38 @@
 -->
 
 <resources>
-    <dimen name="app_icon_size">64dp</dimen>
-
 <!-- Dynamic Grid -->
-    <dimen name="dynamic_grid_edge_margin">6dp</dimen>
-    <dimen name="dynamic_grid_search_bar_height">48dp</dimen>
-    <!-- We want 46dp extra for the tall search bar. -->
-    <dimen name="dynamic_grid_search_bar_height_tall">94dp</dimen>
-    <dimen name="qsb_internal_padding_top">8dp</dimen>
-    <dimen name="qsb_internal_padding_bottom">8dp</dimen>
-    <dimen name="dynamic_grid_search_bar_extra_top_padding">0dp</dimen>
-    <!-- Reduce the space between the status bar and the search bar when the search bar is tall -->
-    <dimen name="dynamic_grid_search_bar_negative_top_padding_short">-4dp</dimen>
-    <dimen name="dynamic_grid_search_bar_bottom_padding">4dp</dimen>
-    <!-- Reduce the padding between the search bar and workspace when the search bar is tall -->
-    <dimen name="dynamic_grid_search_bar_bottom_negative_padding_short">-6dp</dimen>
-    <dimen name="dynamic_grid_search_bar_bottom_padding_tablet">16dp</dimen>
-    <dimen name="dynamic_grid_page_indicator_height">20dp</dimen>
-    <dimen name="dynamic_grid_icon_drawable_padding">4dp</dimen>
-    <dimen name="dynamic_grid_workspace_page_spacing">8dp</dimen>
+    <dimen name="dynamic_grid_edge_margin">8dp</dimen>
+    <dimen name="dynamic_grid_page_indicator_height">28dp</dimen>
+    <dimen name="dynamic_grid_page_indicator_line_height">1dp</dimen>
+    <dimen name="dynamic_grid_page_indicator_gutter_width_left_nav_bar">38dp</dimen>
+    <dimen name="dynamic_grid_page_indicator_gutter_width_right_nav_bar">48dp</dimen>
+    <dimen name="dynamic_grid_icon_drawable_padding">8dp</dimen>
     <dimen name="dynamic_grid_overview_min_icon_zone_height">80dp</dimen>
     <dimen name="dynamic_grid_overview_max_icon_zone_height">120dp</dimen>
     <dimen name="dynamic_grid_overview_bar_item_width">80dp</dimen>
-    <dimen name="dynamic_grid_overview_bar_spacer_width">20dp</dimen>
+    <dimen name="dynamic_grid_overview_bar_spacer_width">25dp</dimen>
+    <dimen name="dynamic_grid_hotseat_height">88dp</dimen>
+    <dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
+    <dimen name="dynamic_grid_hotseat_gutter_width">24dp</dimen>
+    <dimen name="dynamic_grid_workspace_top_padding">12dp</dimen>
+    <dimen name="dynamic_grid_workspace_page_spacing">8dp</dimen>
+    <!-- Minimum space between workspace and hotseat in spring loaded mode -->
+    <dimen name="dynamic_grid_min_spring_loaded_space">8dp</dimen>
+    <dimen name="dynamic_grid_container_land_left_padding">118dp</dimen>
+    <dimen name="dynamic_grid_container_land_right_padding">66dp</dimen>
+
+<!-- Drop target bar -->
+    <dimen name="dynamic_grid_drop_target_size">48dp</dimen>
+    <dimen name="vert_drop_target_vertical_gap">20dp</dimen>
+    <dimen name="vert_drop_target_horizontal_gap">14dp</dimen>
 
 <!-- App Widget resize frame -->
     <dimen name="default_widget_padding">8dp</dimen>
     <dimen name="widget_handle_margin">13dp</dimen>
     <dimen name="resize_frame_background_padding">24dp</dimen>
 
-<!-- Cling -->
-    <dimen name="cling_migration_logo_height">240dp</dimen>
-    <dimen name="cling_migration_logo_width">165dp</dimen>
-    <dimen name="cling_migration_bg_size">400dp</dimen>
-    <dimen name="cling_migration_bg_shift">-200dp</dimen>
-    <dimen name="cling_migration_content_margin">16dp</dimen>
-    <dimen name="cling_migration_content_width">280dp</dimen>
-
-<!-- Workspace -->
-    <dimen name="workspace_max_gap">16dp</dimen>
-
-<!-- QSB -->
-    <dimen name="toolbar_button_vertical_padding">4dip</dimen>
-    <dimen name="toolbar_button_horizontal_padding">12dip</dimen>
-
 <!-- Container -->
-    <item name="container_margin" format="fraction" type="fraction">0%</item>
-    <dimen name="container_min_margin">8dp</dimen>
-    <dimen name="container_max_width">0dp</dimen>
-
     <!-- Note: This needs to match the fixed insets for the search box. -->
     <dimen name="container_bounds_inset">8dp</dimen>
     <!-- Notes: container_bounds_inset - quantum_panel_outer_padding -->
@@ -82,41 +65,37 @@
     <dimen name="all_apps_grid_section_y_offset">8dp</dimen>
     <dimen name="all_apps_grid_section_text_size">24sp</dimen>
     <dimen name="all_apps_search_bar_height">60dp</dimen>
-    <dimen name="all_apps_search_bar_prediction_bar_padding">8dp</dimen>
-    <dimen name="all_apps_icon_top_bottom_padding">8dp</dimen>
-    <dimen name="all_apps_icon_width_gap">24dp</dimen>
-    <!-- The top padding should account for the existing all_apps_list_top_bottom_padding -->
-    <dimen name="all_apps_prediction_icon_top_padding">8dp</dimen>
-    <dimen name="all_apps_prediction_icon_bottom_padding">18dp</dimen>
-    <dimen name="all_apps_list_top_bottom_padding">8dp</dimen>
+    <dimen name="all_apps_search_bar_margin_top">12dp</dimen>
+    <dimen name="all_apps_search_bar_icon_margin_right">4dp</dimen>
+    <dimen name="all_apps_search_bar_icon_margin_top">1dp</dimen>
+    <dimen name="all_apps_list_bottom_padding">8dp</dimen>
     <dimen name="all_apps_empty_search_message_top_offset">40dp</dimen>
     <dimen name="all_apps_empty_search_bg_top_offset">144dp</dimen>
     <dimen name="all_apps_background_canvas_width">700dp</dimen>
     <dimen name="all_apps_background_canvas_height">475dp</dimen>
+    <dimen name="all_apps_caret_stroke_width">2dp</dimen>
+    <dimen name="all_apps_caret_shadow_spread">1dp</dimen>
+    <dimen name="all_apps_caret_size">13dp</dimen>
+    <dimen name="all_apps_caret_workspace_offset">4dp</dimen>
 
-    <!-- Search bar in All Apps -->
-    <dimen name="all_apps_header_max_elevation">4dp</dimen>
+<!-- Search bar in All Apps -->
+    <dimen name="all_apps_header_max_elevation">3dp</dimen>
     <dimen name="all_apps_header_scroll_to_elevation">16dp</dimen>
     <dimen name="all_apps_header_shadow_height">6dp</dimen>
 
-    <!-- The overflow is used to create a bottom border, by drawing other three sides
-        outside the bounds. Ensure that:
-            all_apps_search_bar_bg_overflow < (-3 * all_apps_search_bar_divider_width)
-        -6dp is picked at random, any smaller value would do.
-    -->
-    <dimen name="all_apps_search_bar_bg_overflow">-6dp</dimen>
-    <dimen name="all_apps_search_bar_divider_width">1dp</dimen>
+    <dimen name="all_apps_divider_margin_vertical">8dp</dimen>
 
-    <!-- Widget tray -->
-    <dimen name="widget_container_inset">8dp</dimen>
+    <dimen name="all_apps_bezel_swipe_height">24dp</dimen>
+
+<!-- Widget tray -->
     <dimen name="widget_preview_label_vertical_padding">8dp</dimen>
     <dimen name="widget_preview_label_horizontal_padding">8dp</dimen>
-    <dimen name="widget_preview_horizontal_padding">8dp</dimen>
 
     <dimen name="widget_section_height">56dp</dimen>
     <dimen name="widget_section_icon_size">40dp</dimen>
     <dimen name="widget_section_vertical_padding">8dp</dimen>
     <dimen name="widget_section_horizontal_padding">16dp</dimen>
+    <dimen name="widget_section_indent">0dp</dimen>
 
     <dimen name="widget_row_padding">8dp</dimen>
     <dimen name="widget_row_divider">2dp</dimen>
@@ -131,6 +110,9 @@
     <dimen name="drop_target_drag_padding">14dp</dimen>
     <dimen name="drop_target_text_size">14sp</dimen>
 
+    <!-- the distance an icon must be dragged before button drop targets accept it -->
+    <dimen name="drag_distanceThreshold">30dp</dimen>
+
     <!-- the area at the edge of the screen that makes the workspace go left
          or right while you're dragging. -->
     <dimen name="scroll_zone">20dp</dimen>
@@ -143,12 +125,24 @@
          and drop targets like all-apps and folders -->
     <dimen name="drag_elevation">30dp</dimen>
 
+    <dimen name="drag_flingToDeleteMinVelocity">-1500dp</dimen>
+
+    <dimen name="spring_loaded_panel_border">1dp</dimen>
+
 <!-- Theme -->
     <dimen name="quantum_panel_outer_padding">4dp</dimen>
 
 <!-- Folders -->
-    <!-- The amount that the preview contents are inset from the preview background -->
-    <dimen name="folder_preview_padding">4dp</dimen>
+    <!-- The size of the padding on the preview background drawable -->
+    <dimen name="folder_preview_padding">6dp</dimen>
+    <dimen name="page_indicator_dot_size">8dp</dimen>
+
+    <dimen name="folder_cell_x_padding">9dp</dimen>
+    <dimen name="folder_cell_y_padding">6dp</dimen>
+    <dimen name="folder_child_text_size">13sp</dimen>
+    <dimen name="folder_label_padding_top">4dp</dimen>
+    <dimen name="folder_label_padding_bottom">12dp</dimen>
+    <dimen name="folder_label_text_size">14sp</dimen>
 
 <!-- Sizes for managed profile badges -->
     <dimen name="profile_badge_size">24dp</dimen>
@@ -164,4 +158,33 @@
 <!-- Pending widget -->
     <dimen name="pending_widget_min_padding">8dp</dimen>
     <dimen name="pending_widget_elevation">2dp</dimen>
+
+<!-- Deep shortcuts -->
+    <dimen name="deep_shortcuts_elevation">9dp</dimen>
+    <dimen name="bg_pill_width">208dp</dimen>
+    <dimen name="bg_pill_height">48dp</dimen>
+    <dimen name="bg_pill_radius">24dp</dimen>
+    <dimen name="deep_shortcuts_spacing">4dp</dimen>
+    <dimen name="deferred_drag_view_scale">6dp</dimen>
+    <!-- an icon with shortcuts must be dragged this far before the container is removed. -->
+    <dimen name="deep_shortcuts_start_drag_threshold">16dp</dimen>
+    <dimen name="deep_shortcut_icon_size">36dp</dimen>
+    <dimen name="deep_shortcut_padding_start">6dp</dimen>
+    <dimen name="deep_shortcut_padding_end">16dp</dimen>
+    <dimen name="deep_shortcut_drawable_padding">8dp</dimen>
+    <dimen name="deep_shortcut_anim_translation_y">5dp</dimen>
+    <dimen name="deep_shortcut_drag_handle_size">16dp</dimen>
+    <dimen name="deep_shortcuts_arrow_width">10dp</dimen>
+    <dimen name="deep_shortcuts_arrow_height">8dp</dimen>
+    <dimen name="deep_shortcuts_arrow_vertical_offset">-2dp</dimen>
+    <!-- deep_shortcut_padding_start + deep_shortcut_icon_size / 2 - deep_shortcuts_arrow_width / 2-->
+    <!-- Note that this works for right-aligned shortcuts, too, because
+         deep_shortcut_padding_end + deep_shortcut_drag_handle_size / 2 - deep_shortcuts_arrow_width / 2
+         also happens to equal 19dp-->
+    <dimen name="deep_shortcuts_arrow_horizontal_offset">19dp</dimen>
+
+<!-- Other -->
+    <!-- Approximates the system status bar height. Not guaranteed to be always be correct. -->
+    <dimen name="status_bar_height">24dp</dimen>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 630e3cd..60a37e5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -21,15 +21,6 @@
     <!-- General -->
     <skip />
 
-    <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
-    <string name="old_launcher_provider_uri" translatable="false">content://com.android.launcher2.settings/favorites?notify=true</string>
-
-    <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
-    <string name="receive_launch_broadcasts_permission" translatable="false">com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS</string>
-
-    <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
-    <string name="receive_first_load_broadcast_permission" translatable="false">com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST</string>
-
     <!-- Application name -->
     <string name="app_name">Launcher3</string>
     <!-- Default folder name -->
@@ -44,6 +35,8 @@
     <string name="safemode_shortcut_error">Downloaded app disabled in Safe mode</string>
     <!-- SafeMode widget error string -->
     <string name="safemode_widget_error">Widgets disabled in Safe mode</string>
+    <!-- Message shown when a shortcut is not available. It could have been temporarily disabled and may start working again after some time. -->
+    <string name="shortcut_not_available">Shortcut isn\'t available</string>
 
     <!-- Widgets -->
     <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
@@ -53,17 +46,18 @@
     <!-- The format string for the dimensions of a widget in the drawer -->
     <!-- There is a special version of this format string for Farsi -->
     <string name="widget_dims_format">%1$d \u00d7 %2$d</string>
+    <!-- Accessibility spoken message format for the dimensions of a widget in the drawer -->
+    <string name="widget_accessible_dims_format">%1$d wide by %2$d high</string>
 
     <!-- All Apps -->
     <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
-    <string name="all_apps_search_bar_hint">Search Apps&#8230;</string>
+    <string name="all_apps_search_bar_hint">Search Apps</string>
     <!-- Loading apps text. [CHAR_LIMIT=50] -->
     <string name="all_apps_loading_message">Loading Apps&#8230;</string>
     <!-- No-search-results text. [CHAR_LIMIT=50] -->
     <string name="all_apps_no_search_results">No Apps found matching \"<xliff:g id="query" example="Android">%1$s</xliff:g>\"</string>
-    <!-- Search market text.  This is a format string where the first argument is the name of the activity
-         handling the search.  The format string does not need to handle both of these arguments. [CHAR_LIMIT=50] -->
-    <string name="all_apps_search_market_message">Go to <xliff:g id="query" example="Play Store">%1$s</xliff:g></string>
+    <!-- Label for the button which allows the user to get app search results. [CHAR_LIMIT=50] -->
+    <string name="all_apps_search_market_message">Search for more apps</string>
 
     <!-- Drag and drop -->
     <skip />
@@ -73,17 +67,17 @@
     <string name="hotseat_out_of_space">No more room in the Favorites tray</string>
 
     <!-- All applications label -->
-    <string name="all_apps_button_label">Apps</string>
+    <string name="all_apps_button_label">Apps list</string>
     <!-- Label for button in all applications label to go back home (to the workspace / desktop)
          for accessibilty (spoken when the button gets focus). -->
     <string name="all_apps_home_button_label">Home</string>
 
-    <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
-    <string name="delete_target_label">Remove</string>
+    <!-- Label for remove drop target. [CHAR_LIMIT=20] -->
+    <string name="remove_drop_target_label">Remove</string>
     <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
-    <string name="delete_target_uninstall_label">Uninstall</string>
-    <!-- Label for the info icon. [CHAR_LIMIT=20] -->
-    <string name="info_target_label">App info</string>
+    <string name="uninstall_drop_target_label">Uninstall</string>
+    <!-- Label for app info drop target. [CHAR_LIMIT=20] -->
+    <string name="app_info_drop_target_label">App info</string>
 
     <!-- Permissions: -->
     <skip />
@@ -122,6 +116,8 @@
     <string name="folder_hint_text">Unnamed Folder</string>
 
     <!-- Accessibility -->
+    <!-- The format string for when an app is temporarily disabled. -->
+    <string name="disabled_app_label">Disabled <xliff:g id="app_name" example="Messenger">%1$s</xliff:g></string>
     <skip />
 
     <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
@@ -131,31 +127,13 @@
     <!-- Description for a new page on homescreen[CHAR_LIMIT=none] -->
     <string name="workspace_new_page">New home screen page</string>
 
-    <!-- Clings -->
-    <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
-    <string name="first_run_cling_title">Welcome</string>
-    <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
-    <string name="migration_cling_title">Copy your app icons</string>
-    <!-- The description of what migration does [CHAR_LIMIT=70] -->
-    <string name="migration_cling_description">Import icons and folders from your old Home screens?</string>
-    <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
-    <string name="migration_cling_copy_apps">COPY ICONS</string>
-    <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
-    <string name="migration_cling_use_default">START FRESH</string>
-    <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
-    <string name="workspace_cling_longpress_title">Wallpapers, widgets, &amp; settings</string>
-    <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
-    <string name="workspace_cling_longpress_description">Touch &amp; hold background to customize</string>
-    <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
-    <string name="workspace_cling_longpress_dismiss">GOT IT</string>
-
     <!-- Folder accessibility -->
     <!-- The format string for when a folder is opened, speaks the dimensions -->
     <string name="folder_opened">Folder opened, <xliff:g id="width" example="5">%1$d</xliff:g> by <xliff:g id="height" example="3">%2$d</xliff:g></string>
     <!-- Instruction that clicking outside will close folder -->
-    <string name="folder_tap_to_close">Touch to close folder</string>
+    <string name="folder_tap_to_close">Tap to close folder</string>
     <!-- Instruction that clicking outside will commit folder rename -->
-    <string name="folder_tap_to_rename">Touch to save rename</string>
+    <string name="folder_tap_to_rename">Tap to save rename</string>
     <!-- Indication that folder closed -->
     <string name="folder_closed">Folder closed</string>
     <!-- Folder renamed format -->
@@ -172,10 +150,16 @@
     <string name="settings_button_text">Settings</string>
     <!-- Message shown when a feature is disabled by the administrator -->
     <string name="msg_disabled_by_admin">Disabled by your admin</string>
+    <!-- Text for custom accessibility action to go to the overview mode, where users can look and change the overall UI of the launcher. -->
+    <string name="accessibility_action_overview">Overview</string>
 
     <!-- Strings for settings -->
     <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
-    <string name="allow_rotation_title">Allow rotation</string>
+    <string name="allow_rotation_title">Allow Home screen rotation</string>
+    <!-- Text explaining when the home screen will get rotated. [CHAR LIMIT=100] -->
+    <string name="allow_rotation_desc">When phone is rotated</string>
+    <!-- Text explaining that rotation is disabled in Display settings. 'Display' refers to the Display section in system settings [CHAR LIMIT=100] -->
+    <string name="allow_rotation_blocked_desc">Current Display setting doesn\'t permit rotation</string>
 
     <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
     <string name="package_state_unknown">Unknown</string>
@@ -269,4 +253,10 @@
     <!-- Accessibility confirmation for widget resize. -->
     <string name="widget_resized">Widget resized to width <xliff:g id="number" example="2">%1$s</xliff:g> height <xliff:g id="number" example="1">%2$s</xliff:g></string>
 
+    <!-- Accessibility action to show quick actions menu for an icon. [CHAR_LIMIT=30] -->
+    <string name="action_deep_shortcut">Shortcuts</string>
+
+    <!-- Accessibility description for the shortcuts menu shown for an app. -->
+    <string name="shortcuts_menu_description"><xliff:g id="number_of_shortcuts" example="3">%1$d</xliff:g> shortcuts for <xliff:g id="app_name" example="Messenger">%2$s</xliff:g></string>
+
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 4eee130..cd06b75 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -18,14 +18,35 @@
 -->
 
 <resources>
-    <style name="Theme.Light.CustomOverscroll" parent="@android:style/Theme.DeviceDefault">
+    <!-- Launcher theme -->
+    <style name="BaseLauncherTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:colorBackgroundCacheHint">@null</item>
+        <item name="android:windowShowWallpaper">true</item>
+        <item name="android:windowNoTitle">true</item>
+    </style>
+
+    <style name="LauncherTheme" parent="@style/BaseLauncherTheme"></style>
+
+    <style name="Theme" parent="@style/LauncherTheme"></style>
+
+    <!-- Theme for the widget container. Overridden on API 25. -->
+    <style name="WidgetContainerTheme" parent="@android:style/Theme.DeviceDefault.Settings">
+        <item name="colorSecondary">@color/fallback_secondary_color</item>
+    </style>
+
+    <!-- Overscroll effect -->
+    <style name="CustomOverscroll" />
+
+    <style name="CustomOverscroll.Light" parent="@android:style/Theme.DeviceDefault.Light">
         <item name="android:colorEdgeEffect">@color/folder_edge_effect_color</item>
     </style>
 
-    <style name="Theme.Dark.CustomOverscroll" parent="@android:style/Theme.DeviceDefault">
+    <style name="CustomOverscroll.Dark">
         <item name="android:colorEdgeEffect">@color/workspace_edge_effect_color</item>
     </style>
 
+    <!-- Different icons -->
     <style name="Icon">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">match_parent</item>
@@ -53,24 +74,35 @@
         <item name="android:background">@null</item>
         <item name="android:textColor">@color/quantum_panel_text_color</item>
         <item name="android:shadowRadius">0</item>
+        <item name="android:textSize">@dimen/folder_child_text_size</item>
+        <item name="android:gravity">center_horizontal</item>
+        <item name="android:includeFontPadding">false</item>
         <item name="customShadows">false</item>
+        <item name="iconDisplay">folder</item>
     </style>
 
-    <style name="SearchButton"></style>
-
-    <style name="DropTargetButtonContainer">
-        <item name="android:layout_width">0dp</item>
-        <item name="android:layout_height">match_parent</item>
+    <style name="Icon.DeepShortcut">
+        <item name="android:gravity">start|center_vertical</item>
+        <item name="android:textAlignment">viewStart</item>
+        <item name="android:elevation">@dimen/deep_shortcuts_elevation</item>
+        <item name="android:paddingStart">@dimen/bg_pill_height</item>
+        <item name="android:paddingEnd">@dimen/deep_shortcut_padding_end</item>
+        <item name="android:drawableEnd">@drawable/deep_shortcuts_drag_handle</item>
+        <item name="android:drawablePadding">@dimen/deep_shortcut_drawable_padding</item>
+        <item name="android:textColor">#FF212121</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:fontFamily">sans-serif</item>
+        <item name="android:shadowRadius">0</item>
+        <item name="customShadows">false</item>
+        <item name="layoutHorizontal">true</item>
+        <item name="iconSizeOverride">@dimen/deep_shortcut_icon_size</item>
     </style>
 
+    <!-- Drop targets -->
     <style name="DropTargetButtonBase">
-        <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>
-        <item name="android:paddingLeft">25dp</item>
-        <item name="android:paddingRight">25dp</item>
+        <item name="android:paddingLeft">16dp</item>
+        <item name="android:paddingRight">16dp</item>
         <item name="android:textColor">#FFFFFFFF</item>
         <item name="android:textSize">@dimen/drop_target_text_size</item>
         <item name="android:singleLine">true</item>
@@ -83,6 +115,7 @@
 
     <style name="DropTargetButton" parent="DropTargetButtonBase" />
 
+    <!-- Virtual preloaders -->
     <style name="PreloadIcon">
         <item name="background">@drawable/virtual_preload</item>
         <item name="indicatorSize">4dp</item>
@@ -94,5 +127,4 @@
         <item name="indicatorSize">4dp</item>
         <item name="ringOutset">4dp</item>
     </style>
-
 </resources>
diff --git a/res/xml/backupscheme.xml b/res/xml/backupscheme.xml
new file mode 100644
index 0000000..7e833a0
--- /dev/null
+++ b/res/xml/backupscheme.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<full-backup-content xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <include domain="database" path="launcher.db" />
+    <include domain="sharedpref" path="com.android.launcher3.prefs.xml" />
+
+</full-backup-content>
\ No newline at end of file
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
new file mode 100644
index 0000000..aeda1a2
--- /dev/null
+++ b/res/xml/device_profiles.xml
@@ -0,0 +1,196 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 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.
+-->
+
+<profiles xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3" >
+
+    <profile
+        launcher:name="Super Short Stubby"
+        launcher:minWidthDps="255"
+        launcher:minHeightDps="300"
+        launcher:numRows="2"
+        launcher:numColumns="3"
+        launcher:numFolderRows="2"
+        launcher:numFolderColumns="3"
+        launcher:minAllAppsPredictionColumns="3"
+        launcher:iconSize="48"
+        launcher:iconTextSize="13.0"
+        launcher:numHotseatIcons="3"
+        launcher:hotseatIconSize="48"
+        launcher:defaultLayoutId="@xml/default_workspace_3x3"
+        />
+
+    <profile
+        launcher:name="Shorter Stubby"
+        launcher:minWidthDps="255"
+        launcher:minHeightDps="400"
+        launcher:numRows="3"
+        launcher:numColumns="3"
+        launcher:numFolderRows="3"
+        launcher:numFolderColumns="3"
+        launcher:minAllAppsPredictionColumns="3"
+        launcher:iconSize="48"
+        launcher:iconTextSize="13.0"
+        launcher:numHotseatIcons="3"
+        launcher:hotseatIconSize="48"
+        launcher:defaultLayoutId="@xml/default_workspace_3x3"
+        />
+
+    <profile
+        launcher:name="Short Stubby"
+        launcher:minWidthDps="275"
+        launcher:minHeightDps="420"
+        launcher:numRows="3"
+        launcher:numColumns="4"
+        launcher:numFolderRows="3"
+        launcher:numFolderColumns="4"
+        launcher:minAllAppsPredictionColumns="4"
+        launcher:iconSize="48"
+        launcher:iconTextSize="13.0"
+        launcher:numHotseatIcons="5"
+        launcher:hotseatIconSize="48"
+        launcher:defaultLayoutId="@xml/default_workspace_4x4"
+        />
+
+    <profile
+        launcher:name="Stubby"
+        launcher:minWidthDps="255"
+        launcher:minHeightDps="450"
+        launcher:numRows="3"
+        launcher:numColumns="4"
+        launcher:numFolderRows="3"
+        launcher:numFolderColumns="4"
+        launcher:minAllAppsPredictionColumns="4"
+        launcher:iconSize="48"
+        launcher:iconTextSize="13.0"
+        launcher:numHotseatIcons="5"
+        launcher:hotseatIconSize="48"
+        launcher:defaultLayoutId="@xml/default_workspace_4x4"
+        />
+
+    <profile
+        launcher:name="Nexus S"
+        launcher:minWidthDps="296"
+        launcher:minHeightDps="491.33"
+        launcher:numRows="4"
+        launcher:numColumns="4"
+        launcher:numFolderRows="4"
+        launcher:numFolderColumns="4"
+        launcher:minAllAppsPredictionColumns="4"
+        launcher:iconSize="48"
+        launcher:iconTextSize="13.0"
+        launcher:numHotseatIcons="5"
+        launcher:hotseatIconSize="48"
+        launcher:defaultLayoutId="@xml/default_workspace_4x4"
+        />
+
+    <profile
+        launcher:name="Nexus 4"
+        launcher:minWidthDps="359"
+        launcher:minHeightDps="567"
+        launcher:numRows="4"
+        launcher:numColumns="4"
+        launcher:numFolderRows="4"
+        launcher:numFolderColumns="4"
+        launcher:minAllAppsPredictionColumns="4"
+        launcher:iconSize="60"
+        launcher:iconTextSize="13.0"
+        launcher:numHotseatIcons="5"
+        launcher:hotseatIconSize="56"
+        launcher:defaultLayoutId="@xml/default_workspace_4x4"
+        />
+
+    <profile
+        launcher:name="Nexus 5"
+        launcher:minWidthDps="335"
+        launcher:minHeightDps="567"
+        launcher:numRows="4"
+        launcher:numColumns="4"
+        launcher:numFolderRows="4"
+        launcher:numFolderColumns="4"
+        launcher:minAllAppsPredictionColumns="4"
+        launcher:iconSize="60"
+        launcher:iconTextSize="13.0"
+        launcher:numHotseatIcons="5"
+        launcher:hotseatIconSize="56"
+        launcher:defaultLayoutId="@xml/default_workspace_4x4"
+        />
+
+    <profile
+        launcher:name="Large Phone"
+        launcher:minWidthDps="406"
+        launcher:minHeightDps="694"
+        launcher:numRows="5"
+        launcher:numColumns="5"
+        launcher:numFolderRows="4"
+        launcher:numFolderColumns="4"
+        launcher:minAllAppsPredictionColumns="4"
+        launcher:iconSize="64"
+        launcher:iconTextSize="14.4"
+        launcher:numHotseatIcons="5"
+        launcher:hotseatIconSize="56"
+        launcher:defaultLayoutId="@xml/default_workspace_5x5"
+        />
+
+    <profile
+        launcher:name="Nexus 7"
+        launcher:minWidthDps="575"
+        launcher:minHeightDps="904"
+        launcher:numRows="5"
+        launcher:numColumns="6"
+        launcher:numFolderRows="4"
+        launcher:numFolderColumns="5"
+        launcher:minAllAppsPredictionColumns="4"
+        launcher:iconSize="72"
+        launcher:iconTextSize="14.4"
+        launcher:numHotseatIcons="7"
+        launcher:hotseatIconSize="60"
+        launcher:defaultLayoutId="@xml/default_workspace_5x6"
+        />
+
+    <profile
+        launcher:name="Nexus 10"
+        launcher:minWidthDps="727"
+        launcher:minHeightDps="1207"
+        launcher:numRows="5"
+        launcher:numColumns="6"
+        launcher:numFolderRows="4"
+        launcher:numFolderColumns="5"
+        launcher:minAllAppsPredictionColumns="4"
+        launcher:iconSize="76"
+        launcher:iconTextSize="14.4"
+        launcher:numHotseatIcons="7"
+        launcher:hotseatIconSize="76"
+        launcher:defaultLayoutId="@xml/default_workspace_5x6"
+        />
+
+    <profile
+        launcher:name="20-inch Tablet"
+        launcher:minWidthDps="1527"
+        launcher:minHeightDps="2527"
+        launcher:numRows="7"
+        launcher:numColumns="7"
+        launcher:numFolderRows="6"
+        launcher:numFolderColumns="6"
+        launcher:minAllAppsPredictionColumns="4"
+        launcher:iconSize="100"
+        launcher:iconTextSize="20.0"
+        launcher:numHotseatIcons="7"
+        launcher:hotseatIconSize="72"
+        launcher:defaultLayoutId="@xml/default_workspace_5x6"
+        />
+
+</profiles>
\ No newline at end of file
diff --git a/src/com/android/launcher3/Alarm.java b/src/com/android/launcher3/Alarm.java
index e9f1fd9..d5b434c 100644
--- a/src/com/android/launcher3/Alarm.java
+++ b/src/com/android/launcher3/Alarm.java
@@ -17,6 +17,7 @@
 package com.android.launcher3;
 
 import android.os.Handler;
+import android.os.SystemClock;
 
 public class Alarm implements Runnable{
     // if we reach this time and the alarm hasn't been cancelled, call the listener
@@ -41,9 +42,16 @@
     // Sets the alarm to go off in a certain number of milliseconds. If the alarm is already set,
     // it's overwritten and only the new alarm setting is used
     public void setAlarm(long millisecondsInFuture) {
-        long currentTime = System.currentTimeMillis();
+        long currentTime = SystemClock.uptimeMillis();
         mAlarmPending = true;
+        long oldTriggerTime = mAlarmTriggerTime;
         mAlarmTriggerTime = currentTime + millisecondsInFuture;
+
+        // If the previous alarm was set for a longer duration, cancel it.
+        if (mWaitingForCallback && oldTriggerTime > mAlarmTriggerTime) {
+            mHandler.removeCallbacks(this);
+            mWaitingForCallback = false;
+        }
         if (!mWaitingForCallback) {
             mHandler.postDelayed(this, mAlarmTriggerTime - currentTime);
             mWaitingForCallback = true;
@@ -51,15 +59,14 @@
     }
 
     public void cancelAlarm() {
-        mAlarmTriggerTime = 0;
         mAlarmPending = false;
     }
 
     // this is called when our timer runs out
     public void run() {
         mWaitingForCallback = false;
-        if (mAlarmTriggerTime != 0) {
-            long currentTime = System.currentTimeMillis();
+        if (mAlarmPending) {
+            long currentTime = SystemClock.uptimeMillis();
             if (mAlarmTriggerTime > currentTime) {
                 // We still need to wait some time to trigger spring loaded mode--
                 // post a new callback
diff --git a/src/com/android/launcher3/AnotherWindowDropTarget.java b/src/com/android/launcher3/AnotherWindowDropTarget.java
new file mode 100644
index 0000000..0e18874
--- /dev/null
+++ b/src/com/android/launcher3/AnotherWindowDropTarget.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.graphics.Rect;
+
+/**
+ * Drop target used when another window (i.e. another process) has accepted a global system drag.
+ * If the accepted item was a shortcut, we delete it from Launcher.
+ */
+public class AnotherWindowDropTarget implements DropTarget {
+    final Launcher mLauncher;
+
+    public AnotherWindowDropTarget (Context context) { mLauncher = (Launcher) context; }
+
+    @Override
+    public boolean isDropEnabled() { return true; }
+
+    @Override
+    public void onDrop(DragObject dragObject) {
+        dragObject.deferDragViewCleanupPostAnimation = false;
+        LauncherModel.deleteItemFromDatabase(mLauncher, (ShortcutInfo) dragObject.dragInfo);
+    }
+
+    @Override
+    public void onDragEnter(DragObject dragObject) {}
+
+    @Override
+    public void onDragOver(DragObject dragObject) {}
+
+    @Override
+    public void onDragExit(DragObject dragObject) {}
+
+    @Override
+    public void onFlingToDelete(DragObject dragObject, PointF vec) {}
+
+    @Override
+    public boolean acceptDrop(DragObject dragObject) {
+        return dragObject.dragInfo instanceof ShortcutInfo;
+    }
+
+    @Override
+    public void prepareAccessibilityDrop() {}
+
+    // These methods are implemented in Views
+    @Override
+    public void getHitRectRelativeToDragLayer(Rect outRect) {}
+}
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index c7a6ae4..4c4d67c 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -29,13 +29,11 @@
 import com.android.launcher3.util.PackageManagerHelper;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 
 /**
  * Represents an app in AllAppsView.
  */
 public class AppInfo extends ItemInfo {
-    private static final String TAG = "Launcher3.AppInfo";
 
     /**
      * The intent used to start the application.
@@ -64,10 +62,11 @@
      */
     int isDisabled = ShortcutInfo.DEFAULT;
 
-    AppInfo() {
+    public AppInfo() {
         itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
     }
 
+    @Override
     public Intent getIntent() {
         return intent;
     }
@@ -126,12 +125,8 @@
     }
 
     @Override
-    public String toString() {
-        return "ApplicationInfo(title=" + title + " id=" + this.id
-                + " type=" + this.itemType + " container=" + this.container
-                + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
-                + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
-                + " user=" + user + ")";
+    protected String dumpProperties() {
+        return super.dumpProperties() + " componentName=" + componentName;
     }
 
     /**
@@ -140,7 +135,7 @@
     public static void dumpApplicationInfoList(String tag, String label, ArrayList<AppInfo> list) {
         Log.d(tag, label + " size=" + list.size());
         for (AppInfo info: list) {
-            Log.d(tag, "   title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap 
+            Log.d(tag, "   title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap
                     + " componentName=" + info.componentName.getPackageName());
         }
     }
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 6b7ff88..cd27b4c 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -1,5 +1,7 @@
 package com.android.launcher3;
 
+import com.android.launcher3.dragndrop.DragLayer;
+
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
@@ -9,6 +11,7 @@
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.view.Gravity;
 import android.view.KeyEvent;
@@ -24,7 +27,10 @@
     private static final float DIMMED_HANDLE_ALPHA = 0f;
     private static final float RESIZE_THRESHOLD = 0.66f;
 
-    private static Rect sTmpRect = new Rect();
+    private static final Rect sTmpRect = new Rect();
+
+    // Represents the cell size on the grid in the two orientations.
+    private static Point[] sCellSize;
 
     private final Launcher mLauncher;
     private final LauncherAppWidgetHostView mWidgetView;
@@ -74,7 +80,7 @@
             LauncherAppWidgetHostView widgetView, CellLayout cellLayout, DragLayer dragLayer) {
 
         super(context);
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
         mCellLayout = cellLayout;
         mWidgetView = widgetView;
         LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo)
@@ -357,29 +363,28 @@
                 sTmpRect.right, sTmpRect.bottom);
     }
 
-    public static Rect getWidgetSizeRanges(Launcher launcher, int spanX, int spanY, Rect rect) {
+    public static Rect getWidgetSizeRanges(Context context, int spanX, int spanY, Rect rect) {
+        if (sCellSize == null) {
+            InvariantDeviceProfile inv = LauncherAppState.getInstance().getInvariantDeviceProfile();
+
+            // Initiate cell sizes.
+            sCellSize = new Point[2];
+            sCellSize[0] = inv.landscapeProfile.getCellSize();
+            sCellSize[1] = inv.portraitProfile.getCellSize();
+        }
+
         if (rect == null) {
             rect = new Rect();
         }
-        Rect landMetrics = Workspace.getCellLayoutMetrics(launcher, CellLayout.LANDSCAPE);
-        Rect portMetrics = Workspace.getCellLayoutMetrics(launcher, CellLayout.PORTRAIT);
-        final float density = launcher.getResources().getDisplayMetrics().density;
+        final float density = context.getResources().getDisplayMetrics().density;
 
         // Compute landscape size
-        int cellWidth = landMetrics.left;
-        int cellHeight = landMetrics.top;
-        int widthGap = landMetrics.right;
-        int heightGap = landMetrics.bottom;
-        int landWidth = (int) ((spanX * cellWidth + (spanX - 1) * widthGap) / density);
-        int landHeight = (int) ((spanY * cellHeight + (spanY - 1) * heightGap) / density);
+        int landWidth = (int) ((spanX * sCellSize[0].x) / density);
+        int landHeight = (int) ((spanY * sCellSize[0].y) / density);
 
         // Compute portrait size
-        cellWidth = portMetrics.left;
-        cellHeight = portMetrics.top;
-        widthGap = portMetrics.right;
-        heightGap = portMetrics.bottom;
-        int portWidth = (int) ((spanX * cellWidth + (spanX - 1) * widthGap) / density);
-        int portHeight = (int) ((spanY * cellHeight + (spanY - 1) * heightGap) / density);
+        int portWidth = (int) ((spanX * sCellSize[1].x) / density);
+        int portHeight = (int) ((spanY * sCellSize[1].y) / density);
         rect.set(portWidth, landHeight, landWidth, portHeight);
         return rect;
     }
@@ -458,10 +463,10 @@
             PropertyValuesHolder y = PropertyValuesHolder.ofInt("y", lp.y, newY);
             ObjectAnimator oa =
                     LauncherAnimUtils.ofPropertyValuesHolder(lp, this, width, height, x, y);
-            ObjectAnimator leftOa = LauncherAnimUtils.ofFloat(mLeftHandle, "alpha", 1.0f);
-            ObjectAnimator rightOa = LauncherAnimUtils.ofFloat(mRightHandle, "alpha", 1.0f);
-            ObjectAnimator topOa = LauncherAnimUtils.ofFloat(mTopHandle, "alpha", 1.0f);
-            ObjectAnimator bottomOa = LauncherAnimUtils.ofFloat(mBottomHandle, "alpha", 1.0f);
+            ObjectAnimator leftOa = LauncherAnimUtils.ofFloat(mLeftHandle, ALPHA, 1.0f);
+            ObjectAnimator rightOa = LauncherAnimUtils.ofFloat(mRightHandle, ALPHA, 1.0f);
+            ObjectAnimator topOa = LauncherAnimUtils.ofFloat(mTopHandle, ALPHA, 1.0f);
+            ObjectAnimator bottomOa = LauncherAnimUtils.ofFloat(mBottomHandle, ALPHA, 1.0f);
             oa.addUpdateListener(new AnimatorUpdateListener() {
                 public void onAnimationUpdate(ValueAnimator animation) {
                     requestLayout();
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 54ce0fd..c5b3104 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -9,17 +9,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
-import android.os.AsyncTask;
 import android.util.Log;
 
 import com.android.launcher3.LauncherSettings.Favorites;
 
-import java.util.ArrayList;
-import java.util.List;
-
 public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
 
-    private static final String TAG = "AppWidgetsRestoredReceiver";
+    private static final String TAG = "AWRestoredReceiver";
 
     @Override
     public void onReceive(Context context, Intent intent) {
@@ -39,8 +35,8 @@
      */
     static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
         final ContentResolver cr = context.getContentResolver();
-        final List<Integer> idsToRemove = new ArrayList<Integer>();
         final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
+        AppWidgetHost appWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
 
         for (int i = 0; i < oldWidgetIds.length; i++) {
             Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]);
@@ -69,28 +65,13 @@
                 try {
                     if (!cursor.moveToFirst()) {
                         // The widget no long exists.
-                        idsToRemove.add(newWidgetIds[i]);
+                        appWidgetHost.deleteAppWidgetId(newWidgetIds[i]);
                     }
                 } finally {
                     cursor.close();
                 }
             }
         }
-        // Unregister the widget IDs which are not present on the workspace. This could happen
-        // when a widget place holder is removed from workspace, before this method is called.
-        if (!idsToRemove.isEmpty()) {
-            final AppWidgetHost appWidgetHost =
-                    new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
-            new AsyncTask<Void, Void, Void>() {
-                public Void doInBackground(Void ... args) {
-                    for (Integer id : idsToRemove) {
-                        appWidgetHost.deleteAppWidgetId(id);
-                        Log.e(TAG, "Widget no longer present, appWidgetId=" + id);
-                    }
-                    return null;
-                }
-            }.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
-        }
 
         LauncherAppState app = LauncherAppState.getInstanceNoCreate();
         if (app != null) {
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 0d71a0c..d5309b4 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -37,6 +37,7 @@
 
 import com.android.launcher3.LauncherProvider.SqlArguments;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.Thunk;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -157,7 +158,7 @@
     protected final Resources mSourceRes;
     protected final int mLayoutId;
 
-    private final int mHotseatAllAppsRank;
+    private final InvariantDeviceProfile mIdp;
     private final int mRowCount;
     private final int mColumnCount;
 
@@ -181,10 +182,9 @@
         mSourceRes = res;
         mLayoutId = layoutId;
 
-        InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
-        mHotseatAllAppsRank = idp.hotseatAllAppsRank;
-        mRowCount = idp.numRows;
-        mColumnCount = idp.numColumns;
+        mIdp = LauncherAppState.getInstance().getInvariantDeviceProfile();
+        mRowCount = mIdp.numRows;
+        mColumnCount = mIdp.numColumns;
     }
 
     /**
@@ -231,7 +231,8 @@
             out[0] = Favorites.CONTAINER_HOTSEAT;
             // Hack: hotseat items are stored using screen ids
             long rank = Long.parseLong(getAttributeValue(parser, ATTR_RANK));
-            out[1] = (rank < mHotseatAllAppsRank) ? rank : (rank + 1);
+            out[1] = (FeatureFlags.NO_ALL_APPS_ICON || rank < mIdp.getAllAppsButtonRank())
+                    ? rank : (rank + 1);
         } else {
             out[0] = Favorites.CONTAINER_DESKTOP;
             out[1] = Long.parseLong(getAttributeValue(parser, ATTR_SCREEN));
@@ -315,7 +316,7 @@
         parsers.put(TAG_APP_ICON, new AppShortcutParser());
         parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
         parsers.put(TAG_FOLDER, new FolderParser());
-        parsers.put(TAG_APPWIDGET, new AppWidgetParser());
+        parsers.put(TAG_APPWIDGET, new PendingWidgetParser());
         parsers.put(TAG_SHORTCUT, new ShortcutParser(mSourceRes));
         return parsers;
     }
@@ -436,7 +437,6 @@
             }
 
             ItemInfo.writeBitmap(mValues, Utilities.createIconBitmap(icon, mContext));
-            mValues.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
             mValues.put(Favorites.ICON_PACKAGE, mIconRes.getResourcePackageName(iconId));
             mValues.put(Favorites.ICON_RESOURCE, mIconRes.getResourceName(iconId));
 
@@ -459,8 +459,12 @@
     /**
      * AppWidget parser: Required attributes packageName, className, spanX and spanY.
      * Options child nodes: <extra key=... value=... />
+     * It adds a pending widget which allows the widget to come later. If there are extras, those
+     * are passed to widget options during bind.
+     * The config activity for the widget (if present) is not shown, so any optional configurations
+     * should be passed as extras and the widget should support reading these widget options.
      */
-    protected class AppWidgetParser implements TagParser {
+    protected class PendingWidgetParser implements TagParser {
 
         @Override
         public long parseAndAdd(XmlResourceParser parser)
@@ -468,27 +472,13 @@
             final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
             final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
             if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) {
-                if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component");
+                if (LOGD) Log.d(TAG, "Skipping invalid <appwidget> with no component");
                 return -1;
             }
 
-            ComponentName cn = new ComponentName(packageName, className);
-            try {
-                mPackageManager.getReceiverInfo(cn, 0);
-            } catch (Exception e) {
-                String[] packages = mPackageManager.currentToCanonicalPackageNames(
-                        new String[] { packageName });
-                cn = new ComponentName(packages[0], className);
-                try {
-                    mPackageManager.getReceiverInfo(cn, 0);
-                } catch (Exception e1) {
-                    if (LOGD) Log.d(TAG, "Can't find widget provider: " + className);
-                    return -1;
-                }
-            }
-
             mValues.put(Favorites.SPANX, getAttributeValue(parser, ATTR_SPAN_X));
             mValues.put(Favorites.SPANY, getAttributeValue(parser, ATTR_SPAN_Y));
+            mValues.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
 
             // Read the extras
             Bundle extras = new Bundle();
@@ -513,38 +503,26 @@
                 }
             }
 
-            final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
-            long insertedId = -1;
-            try {
-                int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+            return verifyAndInsert(new ComponentName(packageName, className), extras);
+        }
 
-                if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn)) {
-                    if (LOGD) Log.e(TAG, "Unable to bind app widget id " + cn);
-                    return -1;
-                }
-
-                mValues.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
-                mValues.put(Favorites.APPWIDGET_ID, appWidgetId);
-                mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
-                mValues.put(Favorites._ID, mCallback.generateNewItemId());
-                insertedId = mCallback.insertAndCheck(mDb, mValues);
-                if (insertedId < 0) {
-                    mAppWidgetHost.deleteAppWidgetId(appWidgetId);
-                    return insertedId;
-                }
-
-                // Send a broadcast to configure the widget
-                if (!extras.isEmpty()) {
-                    Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
-                    intent.setComponent(cn);
-                    intent.putExtras(extras);
-                    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
-                    mContext.sendBroadcast(intent);
-                }
-            } catch (RuntimeException ex) {
-                if (LOGD) Log.e(TAG, "Problem allocating appWidgetId", ex);
+        protected long verifyAndInsert(ComponentName cn, Bundle extras) {
+            mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
+            mValues.put(Favorites.RESTORED,
+                    LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
+                            LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
+                            LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
+            mValues.put(Favorites._ID, mCallback.generateNewItemId());
+            if (!extras.isEmpty()) {
+                mValues.put(Favorites.INTENT, new Intent().putExtras(extras).toUri(0));
             }
-            return insertedId;
+
+            long insertedId = mCallback.insertAndCheck(mDb, mValues);
+            if (insertedId < 0) {
+                return -1;
+            } else {
+                return insertedId;
+            }
         }
     }
 
@@ -630,7 +608,7 @@
                     copyInteger(myValues, childValues, Favorites.CELLY);
 
                     addedId = folderItems.get(0);
-                    mDb.update(LauncherProvider.TABLE_FAVORITES, childValues,
+                    mDb.update(Favorites.TABLE_NAME, childValues,
                             Favorites._ID + "=" + addedId, null);
                 }
             }
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index 538c24a..96942ee 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -18,34 +18,33 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.FrameLayout;
 
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.config.FeatureFlags;
+
 /**
  * A base container view, which supports resizing.
  */
-public abstract class BaseContainerView extends FrameLayout implements Insettable {
+public abstract class BaseContainerView extends FrameLayout
+        implements DeviceProfile.LauncherLayoutChangeListener {
 
-    private final static String TAG = "BaseContainerView";
+    protected int mContainerPaddingLeft;
+    protected int mContainerPaddingRight;
+    protected int mContainerPaddingTop;
+    protected int mContainerPaddingBottom;
 
-    // The window insets
-    private final Rect mInsets = new Rect();
-    // The computed padding to apply to the container to achieve the container bounds
-    protected final Rect mContentPadding = new Rect();
-    // The inset to apply to the edges and between the search bar and the container
-    private final int mContainerBoundsInset;
-
-    private final Drawable mRevealDrawable;
+    private InsetDrawable mRevealDrawable;
+    protected final Drawable mBaseDrawable;
 
     private View mRevealView;
     private View mContent;
 
-    protected final int mHorizontalPadding;
-
     public BaseContainerView(Context context) {
         this(context, null);
     }
@@ -56,93 +55,52 @@
 
     public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mContainerBoundsInset = getResources().getDimensionPixelSize(R.dimen.container_bounds_inset);
 
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.BaseContainerView, defStyleAttr, 0);
-        mRevealDrawable = a.getDrawable(R.styleable.BaseContainerView_revealBackground);
-        a.recycle();
-
-        int maxSize = getResources().getDimensionPixelSize(R.dimen.container_max_width);
-        int minMargin = getResources().getDimensionPixelSize(R.dimen.container_min_margin);
-        int width = ((Launcher) context).getDeviceProfile().availableWidthPx;
-
-        if (maxSize > 0) {
-            mHorizontalPadding = Math.max(minMargin, (width - maxSize) / 2);
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && this instanceof AllAppsContainerView) {
+            mBaseDrawable = new ColorDrawable();
         } else {
-            mHorizontalPadding = Math.max(minMargin,
-                    (int) getResources().getFraction(R.fraction.container_margin, width, 1));
+            TypedArray a = context.obtainStyledAttributes(attrs,
+                    R.styleable.BaseContainerView, defStyleAttr, 0);
+            mBaseDrawable = a.getDrawable(R.styleable.BaseContainerView_revealBackground);
+            a.recycle();
         }
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
+        grid.addLauncherLayoutChangedListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
+        grid.removeLauncherLayoutChangedListener(this);
+    }
+
+    @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
 
         mContent = findViewById(R.id.main_content);
         mRevealView = findViewById(R.id.reveal_view);
+
+        updatePaddings();
     }
 
     @Override
-    final public void setInsets(Rect insets) {
-        mInsets.set(insets);
-        updateBackgroundAndPaddings();
+    public void onLauncherLayoutChanged() {
+        updatePaddings();
     }
 
-    /**
-     * Sets the search bar bounds for this container view to match.
-     */
-    final public void setSearchBarBounds(Rect bounds) {
-        // Post the updates since they can trigger a relayout, and this call can be triggered from
-        // a layout pass itself.
-        post(new Runnable() {
-            @Override
-            public void run() {
-                updateBackgroundAndPaddings();
-            }
-        });
+    public void setRevealDrawableColor(int color) {
+        ((ColorDrawable) mBaseDrawable).setColor(color);
     }
 
-    /**
-     * Update the backgrounds and padding in response to a change in the bounds or insets.
-     */
-    protected void updateBackgroundAndPaddings() {
-        Rect padding;
-        padding = new Rect(
-                mHorizontalPadding,
-                mInsets.top + mContainerBoundsInset,
-                mHorizontalPadding,
-                mInsets.bottom + mContainerBoundsInset
-        );
-
-        // The container padding changed, notify the container.
-        if (!padding.equals(mContentPadding)) {
-            mContentPadding.set(padding);
-            onUpdateBackgroundAndPaddings(padding);
-        }
-    }
-
-    private void onUpdateBackgroundAndPaddings(Rect padding) {
-        // Apply the top-bottom padding to itself so that the launcher transition is
-        // clipped correctly
-        setPadding(0, padding.top, 0, padding.bottom);
-
-        InsetDrawable background = new InsetDrawable(mRevealDrawable,
-                padding.left, 0, padding.right, 0);
-        mRevealView.setBackground(background.getConstantState().newDrawable());
-        mContent.setBackground(background);
-
-        // We let the content have a intent background, but still have full width.
-        // This allows the scroll bar to be used responsive outside the background bounds as well.
-        mContent.setPadding(0, 0, 0, 0);
-
-        Rect bgPadding = new Rect();
-        background.getPadding(bgPadding);
-        onUpdateBgPadding(padding, bgPadding);
-    }
-
-    protected abstract void onUpdateBgPadding(Rect padding, Rect bgPadding);
-
     public final View getContentView() {
         return mContent;
     }
@@ -150,4 +108,36 @@
     public final View getRevealView() {
         return mRevealView;
     }
-}
\ No newline at end of file
+
+    private void updatePaddings() {
+        Context context = getContext();
+        Launcher launcher = Launcher.getLauncher(context);
+
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
+                this instanceof AllAppsContainerView &&
+                !launcher.getDeviceProfile().isVerticalBarLayout()) {
+            mContainerPaddingLeft = mContainerPaddingRight = 0;
+            mContainerPaddingTop = mContainerPaddingBottom = 0;
+        } else {
+            DeviceProfile grid = launcher.getDeviceProfile();
+            int[] padding = grid.getContainerPadding(context);
+            mContainerPaddingLeft = padding[0] + grid.edgeMarginPx;
+            mContainerPaddingRight = padding[1] + grid.edgeMarginPx;
+            if (!launcher.getDeviceProfile().isVerticalBarLayout()) {
+                mContainerPaddingTop = mContainerPaddingBottom = grid.edgeMarginPx;
+            } else {
+                mContainerPaddingTop = mContainerPaddingBottom = 0;
+            }
+        }
+
+        mRevealDrawable = new InsetDrawable(mBaseDrawable,
+                mContainerPaddingLeft, mContainerPaddingTop, mContainerPaddingRight,
+                mContainerPaddingBottom);
+        mRevealView.setBackground(mRevealDrawable);
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && this instanceof AllAppsContainerView) {
+            // Skip updating the content background
+        } else {
+            mContent.setBackground(mRevealDrawable);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index f8ef1e1..45bc940 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -41,21 +41,6 @@
     @Thunk int mDy = 0;
     private float mDeltaThreshold;
 
-    /**
-     * The current scroll state of the recycler view.  We use this in onUpdateScrollbar()
-     * and scrollToPositionAtProgress() to determine the scroll position of the recycler view so
-     * that we can calculate what the scroll bar looks like, and where to jump to from the fast
-     * scroller.
-     */
-    public static class ScrollPositionState {
-        // The index of the first visible row
-        public int rowIndex;
-        // The offset of the first visible row
-        public int rowTopOffset;
-        // The adapter position of the first visible item
-        public int itemPos;
-    }
-
     protected BaseRecyclerViewFastScrollBar mScrollbar;
 
     private int mDownX;
@@ -199,11 +184,7 @@
      * Returns the available scroll height:
      *   AvailableScrollHeight = Total height of the all items - last page height
      */
-    protected int getAvailableScrollHeight(int rowCount) {
-        int totalHeight = getPaddingTop() + getTop(rowCount) + getPaddingBottom();
-        int availableScrollHeight = totalHeight - getVisibleHeight();
-        return availableScrollHeight;
-    }
+    protected abstract int getAvailableScrollHeight();
 
     /**
      * Returns the available scroll bar height:
@@ -247,15 +228,12 @@
      * this by mapping the available scroll area of the recycler view to the available space for the
      * scroll bar.
      *
-     * @param scrollPosState the current scroll position
-     * @param rowCount the number of rows, used to calculate the total scroll height (assumes that
-     *                 all rows are the same height)
+     * @param scrollY the current scroll y
      */
-    protected void synchronizeScrollBarThumbOffsetToViewScroll(ScrollPositionState scrollPosState,
-            int rowCount) {
+    protected void synchronizeScrollBarThumbOffsetToViewScroll(int scrollY,
+            int availableScrollHeight) {
         // Only show the scrollbar if there is height to be scrolled
         int availableScrollBarHeight = getAvailableScrollBarHeight();
-        int availableScrollHeight = getAvailableScrollHeight(rowCount);
         if (availableScrollHeight <= 0) {
             mScrollbar.setThumbOffset(-1, -1);
             return;
@@ -264,18 +242,22 @@
         // Calculate the current scroll position, the scrollY of the recycler view accounts for the
         // view padding, while the scrollBarY is drawn right up to the background padding (ignoring
         // padding)
-        int scrollY = getScrollTop(scrollPosState);
         int scrollBarY = mBackgroundPadding.top +
                 (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
 
         // Calculate the position and size of the scroll bar
-        int scrollBarX;
+        mScrollbar.setThumbOffset(getScrollBarX(), scrollBarY);
+    }
+
+    /**
+     * @return the x position for the scrollbar thumb
+     */
+    protected int getScrollBarX() {
         if (Utilities.isRtl(getResources())) {
-            scrollBarX = mBackgroundPadding.left;
+            return mBackgroundPadding.left;
         } else {
-            scrollBarX = getWidth() - mBackgroundPadding.right - mScrollbar.getThumbWidth();
+            return getWidth() - mBackgroundPadding.right - mScrollbar.getThumbWidth();
         }
-        mScrollbar.setThumbOffset(scrollBarX, scrollBarY);
     }
 
     /**
@@ -291,20 +273,7 @@
      *
      * @return the scroll top of this recycler view.
      */
-    protected int getScrollTop(ScrollPositionState scrollPosState) {
-        return getPaddingTop() + getTop(scrollPosState.rowIndex) -
-                scrollPosState.rowTopOffset;
-    }
-
-    /**
-     * Returns information about the item that the recycler view is currently scrolled to.
-     */
-    protected abstract void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask);
-
-    /**
-     * Returns the top (or y position) of the row at the specified index.
-     */
-    protected abstract int getTop(int rowIndex);
+    public abstract int getCurrentScrollY();
 
     /**
      * Maps the touch (from 0..1) to the adapter position that should be visible.
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
index a680169..3d71632 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
@@ -27,7 +27,6 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.view.MotionEvent;
-import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 
 import com.android.launcher3.util.Thunk;
@@ -82,9 +81,7 @@
         mTrackPaint = new Paint();
         mTrackPaint.setColor(rv.getFastScrollerTrackColor(Color.BLACK));
         mTrackPaint.setAlpha(MAX_TRACK_ALPHA);
-        mThumbInactiveColor = rv.getFastScrollerThumbInactiveColor(
-                res.getColor(R.color.container_fastscroll_thumb_inactive_color));
-        mThumbActiveColor = res.getColor(R.color.container_fastscroll_thumb_active_color);
+        mThumbActiveColor = mThumbInactiveColor = Utilities.getColorAccent(rv.getContext());
         mThumbPaint = new Paint();
         mThumbPaint.setAntiAlias(true);
         mThumbPaint.setColor(mThumbInactiveColor);
@@ -139,11 +136,11 @@
     // Setter/getter for the track bar width for animations
     public void setTrackWidth(int width) {
         mInvalidateRect.set(mThumbOffset.x - mThumbCurvature, 0, mThumbOffset.x + mThumbWidth,
-                mRv.getHeight());
+                mRv.getVisibleHeight());
         mTrackWidth = width;
         updateThumbPath();
         mInvalidateRect.union(mThumbOffset.x - mThumbCurvature, 0, mThumbOffset.x + mThumbWidth,
-                mRv.getHeight());
+                mRv.getVisibleHeight());
         mRv.invalidate(mInvalidateRect);
     }
 
@@ -159,10 +156,6 @@
         return mThumbMaxWidth;
     }
 
-    public float getLastTouchY() {
-        return mLastTouchY;
-    }
-
     public boolean isDraggingThumb() {
         return mIsDragging;
     }
@@ -205,7 +198,7 @@
                 if (mIsDragging) {
                     // Update the fastscroller section name at this touch position
                     int top = mRv.getBackgroundPadding().top;
-                    int bottom = mRv.getHeight() - mRv.getBackgroundPadding().bottom - mThumbHeight;
+                    int bottom = top + mRv.getVisibleHeight() - mThumbHeight;
                     float boundedY = (float) Math.max(top, Math.min(bottom, y - mTouchOffset));
                     String sectionName = mRv.scrollToPositionAtProgress((boundedY - top) /
                             (bottom - top));
@@ -213,6 +206,7 @@
                     mPopup.animateVisibility(!sectionName.isEmpty());
                     mRv.invalidate(mPopup.updateFastScrollerBounds(lastY));
                     mLastTouchY = boundedY;
+                    setThumbOffset(mRv.getScrollBarX(), (int) mLastTouchY);
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -236,7 +230,8 @@
 
         // Draw the scroll bar track and thumb
         if (mTrackPaint.getAlpha() > 0) {
-            canvas.drawRect(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, mRv.getHeight(), mTrackPaint);
+            canvas.drawRect(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth,
+                    mRv.getVisibleHeight(), mTrackPaint);
         }
         canvas.drawPath(mThumbPath, mThumbPaint);
 
@@ -293,7 +288,7 @@
     /**
      * Returns whether the specified points are near the scroll bar bounds.
      */
-    private boolean isNearThumb(int x, int y) {
+    public boolean isNearThumb(int x, int y) {
         mTmpRect.set(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth,
                 mThumbOffset.y + mThumbHeight);
         mTmpRect.inset(mTouchInset, mTouchInset);
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java
index ebaba18..b9e6277 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java
@@ -18,6 +18,7 @@
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
@@ -31,9 +32,16 @@
 
     private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 1.5f;
 
+    private static final int SHADOW_INSET = 3;
+    private static final int SHADOW_SHIFT_Y = 2;
+    private static final float SHADOW_ALPHA_MULTIPLIER = 0.67f;
+
     private Resources mRes;
     private BaseRecyclerView mRv;
 
+    private Bitmap mShadow;
+    private Paint mShadowPaint;
+
     private Drawable mBg;
     // The absolute bounds of the fast scroller bg
     private Rect mBgBounds = new Rect();
@@ -52,13 +60,20 @@
     public BaseRecyclerViewFastScrollPopup(BaseRecyclerView rv, Resources res) {
         mRes = res;
         mRv = rv;
+
         mBgOriginalSize = res.getDimensionPixelSize(R.dimen.container_fastscroll_popup_size);
-        mBg = res.getDrawable(R.drawable.container_fastscroll_popup_bg);
+        mBg = rv.getContext().getDrawable(R.drawable.container_fastscroll_popup_bg);
         mBg.setBounds(0, 0, mBgOriginalSize, mBgOriginalSize);
+
         mTextPaint = new Paint();
         mTextPaint.setColor(Color.WHITE);
         mTextPaint.setAntiAlias(true);
         mTextPaint.setTextSize(res.getDimensionPixelSize(R.dimen.container_fastscroll_popup_text_size));
+
+        mShadowPaint = new Paint();
+        mShadowPaint.setAntiAlias(true);
+        mShadowPaint.setFilterBitmap(true);
+        mShadowPaint.setDither(true);
     }
 
     /**
@@ -75,6 +90,7 @@
 
     /**
      * Updates the bounds for the fast scroller.
+     *
      * @return the invalidation rect for this update.
      */
     public Rect updateFastScrollerBounds(int lastTouchY) {
@@ -96,9 +112,14 @@
             }
             mBgBounds.top = lastTouchY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgHeight);
             mBgBounds.top = Math.max(edgePadding,
-                    Math.min(mBgBounds.top, mRv.getHeight() - edgePadding - bgHeight));
+                    Math.min(mBgBounds.top, mRv.getVisibleHeight() - edgePadding - bgHeight));
             mBgBounds.bottom = mBgBounds.top + bgHeight;
+
+            // Generate a bitmap for a shadow matching these bounds
+            mShadow = HolographicOutlineHelper.obtain(
+                    mRv.getContext()).createMediumDropShadow(mBg, false /* shouldCache */);
         } else {
+            mShadow = null;
             mBgBounds.setEmpty();
         }
 
@@ -138,17 +159,32 @@
 
     public void draw(Canvas c) {
         if (isVisible()) {
-            // Draw the fast scroller popup
+            // Determine the alpha and prepare the canvas
+            final int alpha = (int) (mAlpha * 255);
             int restoreCount = c.save(Canvas.MATRIX_SAVE_FLAG);
             c.translate(mBgBounds.left, mBgBounds.top);
             mTmpRect.set(mBgBounds);
             mTmpRect.offsetTo(0, 0);
+
+            // Expand the rect (with a negative inset), translate it, and draw the shadow
+            if (mShadow != null) {
+                mTmpRect.inset(-SHADOW_INSET * 2, -SHADOW_INSET * 2);
+                mTmpRect.offset(0, SHADOW_SHIFT_Y);
+                mShadowPaint.setAlpha((int) (alpha * SHADOW_ALPHA_MULTIPLIER));
+                c.drawBitmap(mShadow, null, mTmpRect, mShadowPaint);
+                mTmpRect.inset(SHADOW_INSET * 2, SHADOW_INSET * 2);
+                mTmpRect.offset(0, -SHADOW_SHIFT_Y);
+            }
+
+            // Draw the background
             mBg.setBounds(mTmpRect);
-            mBg.setAlpha((int) (mAlpha * 255));
+            mBg.setAlpha(alpha);
             mBg.draw(c);
-            mTextPaint.setAlpha((int) (mAlpha * 255));
+
+            // Draw the text
+            mTextPaint.setAlpha(alpha);
             c.drawText(mSectionName, (mBgBounds.width() - mTextBounds.width()) / 2,
-                    mBgBounds.height() - (mBgBounds.height() - mTextBounds.height()) / 2,
+                    mBgBounds.height() - (mBgBounds.height() / 2) - mTextBounds.exactCenterY(),
                     mTextPaint);
             c.restoreToCount(restoreCount);
         }
diff --git a/src/com/android/launcher3/BorderCropDrawable.java b/src/com/android/launcher3/BorderCropDrawable.java
deleted file mode 100644
index caf497d..0000000
--- a/src/com/android/launcher3/BorderCropDrawable.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-
-public class BorderCropDrawable extends Drawable {
-
-    private final Drawable mChild;
-    private final Rect mBoundsShift;
-    private final Rect mPadding;
-
-    BorderCropDrawable(Drawable child, boolean cropLeft,
-            boolean cropTop, boolean cropRight, boolean cropBottom) {
-        mChild = child;
-
-        mBoundsShift = new Rect();
-        mPadding = new Rect();
-        mChild.getPadding(mPadding);
-
-        if (cropLeft) {
-            mBoundsShift.left = -mPadding.left;
-            mPadding.left = 0;
-        }
-        if (cropTop) {
-            mBoundsShift.top = -mPadding.top;
-            mPadding.top = 0;
-        }
-        if (cropRight) {
-            mBoundsShift.right = mPadding.right;
-            mPadding.right = 0;
-        }
-        if (cropBottom) {
-            mBoundsShift.bottom = mPadding.bottom;
-            mPadding.bottom = 0;
-        }
-    }
-
-    @Override
-    protected void onBoundsChange(Rect bounds) {
-        mChild.setBounds(
-                bounds.left + mBoundsShift.left,
-                bounds.top + mBoundsShift.top,
-                bounds.right + mBoundsShift.right,
-                bounds.bottom + mBoundsShift.bottom);
-    }
-
-    @Override
-    public boolean getPadding(Rect padding) {
-        padding.set(mPadding);
-        return (padding.left | padding.top | padding.right | padding.bottom) != 0;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        mChild.draw(canvas);
-    }
-
-    @Override
-    public int getOpacity() {
-        return mChild.getOpacity();
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        mChild.setAlpha(alpha);
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter cf) {
-        mChild.setColorFilter(cf);
-    }
-}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 09ec60c..a294fa5 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -24,8 +24,8 @@
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.Region;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
@@ -36,10 +36,12 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.ViewDebug;
 import android.view.ViewParent;
 import android.widget.TextView;
 
 import com.android.launcher3.IconCache.IconLoadRequest;
+import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.model.PackageItemInfo;
 
 import java.text.NumberFormat;
@@ -54,18 +56,22 @@
 
     private static SparseArray<Theme> sPreloaderThemes = new SparseArray<Theme>(2);
 
-    private static final float SHADOW_LARGE_RADIUS = 4.0f;
-    private static final float SHADOW_SMALL_RADIUS = 1.75f;
-    private static final float SHADOW_Y_OFFSET = 2.0f;
-    private static final int SHADOW_LARGE_COLOUR = 0xDD000000;
-    private static final int SHADOW_SMALL_COLOUR = 0xCC000000;
+    // Dimensions in DP
+    private static final float AMBIENT_SHADOW_RADIUS = 2.5f;
+    private static final float KEY_SHADOW_RADIUS = 1f;
+    private static final float KEY_SHADOW_OFFSET = 0.5f;
+    private static final int AMBIENT_SHADOW_COLOR = 0x33000000;
+    private static final int KEY_SHADOW_COLOR = 0x66000000;
 
     private static final int DISPLAY_WORKSPACE = 0;
     private static final int DISPLAY_ALL_APPS = 1;
+    private static final int DISPLAY_FOLDER = 2;
 
     private final Launcher mLauncher;
     private Drawable mIcon;
+    private final boolean mCenterVertically;
     private final Drawable mBackground;
+    private OnLongClickListener mOnLongClickListener;
     private final CheckLongPressHelper mLongPressHelper;
     private final HolographicOutlineHelper mOutlineHelper;
     private final StylusEventHelper mStylusEventHelper;
@@ -80,10 +86,14 @@
     private final boolean mCustomShadowsEnabled;
     private final boolean mLayoutHorizontal;
     private final int mIconSize;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private int mTextColor;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mStayPressed;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mIgnorePressedStateChange;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mDisableRelayout = false;
 
     private IconLoadRequest mIconLoadRequest;
@@ -98,7 +108,7 @@
 
     public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
         DeviceProfile grid = mLauncher.getDeviceProfile();
 
         TypedArray a = context.obtainStyledAttributes(attrs,
@@ -113,32 +123,35 @@
         if (display == DISPLAY_WORKSPACE) {
             setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
         } else if (display == DISPLAY_ALL_APPS) {
-            setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.allAppsIconTextSizeSp);
+            setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
+            setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
             defaultIconSize = grid.allAppsIconSizePx;
+        } else if (display == DISPLAY_FOLDER) {
+            setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
         }
+        mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
 
         mIconSize = a.getDimensionPixelSize(R.styleable.BubbleTextView_iconSizeOverride,
                 defaultIconSize);
-
         a.recycle();
 
         if (mCustomShadowsEnabled) {
             // Draw the background itself as the parent is drawn twice.
             mBackground = getBackground();
             setBackground(null);
+
+            // Set shadow layer as the larger shadow to that the textView does not clip the shadow.
+            float density = getResources().getDisplayMetrics().density;
+            setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, AMBIENT_SHADOW_COLOR);
         } else {
             mBackground = null;
         }
 
         mLongPressHelper = new CheckLongPressHelper(this);
-        mStylusEventHelper = new StylusEventHelper(this);
+        mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
 
         mOutlineHelper = HolographicOutlineHelper.obtain(getContext());
-        if (mCustomShadowsEnabled) {
-            setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR);
-        }
-
-        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
+        setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
     }
 
     public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache) {
@@ -147,34 +160,16 @@
 
     public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,
             boolean promiseStateChanged) {
-        Bitmap b = info.getIcon(iconCache);
-
-        FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(b);
-        if (info.isDisabled()) {
-            iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
-        }
-        setIcon(iconDrawable, mIconSize);
-        if (info.contentDescription != null) {
-            setContentDescription(info.contentDescription);
-        }
-        setText(info.title);
+        applyIconAndLabel(info.getIcon(iconCache), info);
         setTag(info);
-
         if (promiseStateChanged || info.isPromise()) {
             applyState(promiseStateChanged);
         }
     }
 
     public void applyFromApplicationInfo(AppInfo info) {
-        FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(info.iconBitmap);
-        if (info.isDisabled()) {
-            iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
-        }
-        setIcon(iconDrawable, mIconSize);
-        setText(info.title);
-        if (info.contentDescription != null) {
-            setContentDescription(info.contentDescription);
-        }
+        applyIconAndLabel(info.iconBitmap, info);
+
         // We don't need to check the info since it's not a ShortcutInfo
         super.setTag(info);
 
@@ -183,11 +178,7 @@
     }
 
     public void applyFromPackageItemInfo(PackageItemInfo info) {
-        setIcon(mLauncher.createIconDrawable(info.iconBitmap), mIconSize);
-        setText(info.title);
-        if (info.contentDescription != null) {
-            setContentDescription(info.contentDescription);
-        }
+        applyIconAndLabel(info.iconBitmap, info);
         // We don't need to check the info since it's not a ShortcutInfo
         super.setTag(info);
 
@@ -195,12 +186,26 @@
         verifyHighRes();
     }
 
+    private void applyIconAndLabel(Bitmap icon, ItemInfo info) {
+        FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(icon);
+        if (info.isDisabled()) {
+            iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
+        }
+        setIcon(iconDrawable);
+        setText(info.title);
+        if (info.contentDescription != null) {
+            setContentDescription(info.isDisabled()
+                    ? getContext().getString(R.string.disabled_app_label, info.contentDescription)
+                    : info.contentDescription);
+        }
+    }
+
     /**
      * Used for measurement only, sets some dummy values on this view.
      */
     public void applyDummyInfo() {
         ColorDrawable d = new ColorDrawable();
-        setIcon(mLauncher.resizeIconDrawable(d), mIconSize);
+        setIcon(mLauncher.resizeIconDrawable(d));
         setText("");
     }
 
@@ -266,13 +271,23 @@
     }
 
     @Override
+    public void setOnLongClickListener(OnLongClickListener l) {
+        super.setOnLongClickListener(l);
+        mOnLongClickListener = l;
+    }
+
+    public OnLongClickListener getOnLongClickListener() {
+        return mOnLongClickListener;
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent event) {
         // Call the superclass onTouchEvent first, because sometimes it changes the state to
         // isPressed() on an ACTION_UP
         boolean result = super.onTouchEvent(event);
 
         // Check for a stylus button press, if it occurs cancel any long press checks.
-        if (mStylusEventHelper.checkAndPerformStylusEvent(event)) {
+        if (mStylusEventHelper.onMotionEvent(event)) {
             mLongPressHelper.cancelLongPress();
             result = true;
         }
@@ -313,6 +328,7 @@
     void setStayPressed(boolean stayPressed) {
         mStayPressed = stayPressed;
         if (!stayPressed) {
+            HolographicOutlineHelper.obtain(getContext()).recycleShadowBitmap(mPressedBackground);
             mPressedBackground = null;
         } else {
             if (mPressedBackground == null) {
@@ -395,13 +411,15 @@
         }
 
         // We enhance the shadow by drawing the shadow twice
-        getPaint().setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR);
+        float density = getResources().getDisplayMetrics().density;
+        getPaint().setShadowLayer(density * AMBIENT_SHADOW_RADIUS, 0, 0, AMBIENT_SHADOW_COLOR);
         super.draw(canvas);
         canvas.save(Canvas.CLIP_SAVE_FLAG);
         canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
                 getScrollX() + getWidth(),
                 getScrollY() + getHeight(), Region.Op.INTERSECT);
-        getPaint().setShadowLayer(SHADOW_SMALL_RADIUS, 0.0f, 0.0f, SHADOW_SMALL_COLOUR);
+        getPaint().setShadowLayer(
+                density * KEY_SHADOW_RADIUS, 0.0f, density * KEY_SHADOW_OFFSET, KEY_SHADOW_COLOR);
         super.draw(canvas);
         canvas.restore();
     }
@@ -419,6 +437,19 @@
     }
 
     @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mCenterVertically) {
+            Paint.FontMetrics fm = getPaint().getFontMetrics();
+            int cellHeightPx = mIconSize + getCompoundDrawablePadding() +
+                    (int) Math.ceil(fm.bottom - fm.top);
+            int height = MeasureSpec.getSize(heightMeasureSpec);
+            setPadding(getPaddingLeft(), (height - cellHeightPx) / 2, getPaddingRight(),
+                    getPaddingBottom());
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
         if (mBackground != null) mBackground.setCallback(null);
@@ -471,7 +502,7 @@
                     preloadDrawable = (PreloadIconDrawable) mIcon;
                 } else {
                     preloadDrawable = new PreloadIconDrawable(mIcon, getPreloaderTheme());
-                    setIcon(preloadDrawable, mIconSize);
+                    setIcon(preloadDrawable);
                 }
 
                 preloadDrawable.setLevel(progressLevel);
@@ -500,21 +531,24 @@
      * Sets the icon for this view based on the layout direction.
      */
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    private Drawable setIcon(Drawable icon, int iconSize) {
+    private void setIcon(Drawable icon) {
         mIcon = icon;
-        if (iconSize != -1) {
-            mIcon.setBounds(0, 0, iconSize, iconSize);
+        if (mIconSize != -1) {
+            mIcon.setBounds(0, 0, mIconSize, mIconSize);
         }
+        applyCompoundDrawables(mIcon);
+    }
+
+    protected void applyCompoundDrawables(Drawable icon) {
         if (mLayoutHorizontal) {
             if (Utilities.ATLEAST_JB_MR1) {
-                setCompoundDrawablesRelative(mIcon, null, null, null);
+                setCompoundDrawablesRelative(icon, null, null, null);
             } else {
-                setCompoundDrawables(mIcon, null, null, null);
+                setCompoundDrawables(icon, null, null, null);
             }
         } else {
-            setCompoundDrawables(null, mIcon, null, null);
+            setCompoundDrawables(null, icon, null, null);
         }
-        return icon;
     }
 
     @Override
@@ -620,6 +654,13 @@
     }
 
     /**
+     * Returns true if the view can show custom shortcuts.
+     */
+    public boolean hasDeepShortcuts() {
+        return !mLauncher.getShortcutIdsForItem((ItemInfo) getTag()).isEmpty();
+    }
+
+    /**
      * Returns the start delay when animating between certain {@link FastBitmapDrawable} states.
      */
     private static int getStartDelayForStateChange(final FastBitmapDrawable.State fromState,
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 40a4678..60a2cc3 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -24,6 +24,8 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.PointF;
@@ -39,6 +41,10 @@
 import android.view.animation.LinearInterpolator;
 import android.widget.TextView;
 
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.util.Thunk;
 
 /**
@@ -47,14 +53,20 @@
 public abstract class ButtonDropTarget extends TextView
         implements DropTarget, DragController.DragListener, OnClickListener {
 
-    private static int DRAG_VIEW_DROP_DURATION = 285;
+    private static final int DRAG_VIEW_DROP_DURATION = 285;
 
-    protected Launcher mLauncher;
+    private final boolean mHideParentOnDisable;
+    protected final Launcher mLauncher;
+
     private int mBottomDragPadding;
-    protected SearchDropTargetBar mSearchDropTargetBar;
+    protected DropTargetBar mDropTargetBar;
 
     /** Whether this drop target is active for the current drag */
     protected boolean mActive;
+    /** Whether an accessible drag is in progress */
+    private boolean mAccessibleDrag;
+    /** An item must be dragged at least this many pixels before this drop target is enabled. */
+    private final int mDragDistanceThreshold;
 
     /** The paint applied to the drag view on hover */
     protected int mHoverColor = 0;
@@ -65,26 +77,28 @@
     private AnimatorSet mCurrentColorAnim;
     @Thunk ColorMatrix mSrcFilter, mDstFilter, mCurrentFilter;
 
-
     public ButtonDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
     public ButtonDropTarget(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        mBottomDragPadding = getResources().getDimensionPixelSize(R.dimen.drop_target_drag_padding);
+        mLauncher = Launcher.getLauncher(context);
+
+        Resources resources = getResources();
+        mBottomDragPadding = resources.getDimensionPixelSize(R.dimen.drop_target_drag_padding);
+
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.ButtonDropTarget, defStyle, 0);
+        mHideParentOnDisable = a.getBoolean(R.styleable.ButtonDropTarget_hideParentOnDisable, false);
+        a.recycle();
+        mDragDistanceThreshold = resources.getDimensionPixelSize(R.dimen.drag_distanceThreshold);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mOriginalTextColor = getTextColors();
-
-        // Remove the text in the Phone UI in landscape
-        DeviceProfile grid = ((Launcher) getContext()).getDeviceProfile();
-        if (grid.isVerticalBarLayout()) {
-            setText("");
-        }
     }
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@@ -100,12 +114,8 @@
         }
     }
 
-    public void setLauncher(Launcher launcher) {
-        mLauncher = launcher;
-    }
-
-    public void setSearchDropTargetBar(SearchDropTargetBar searchDropTargetBar) {
-        mSearchDropTargetBar = searchDropTargetBar;
+    public void setDropTargetBar(DropTargetBar dropTargetBar) {
+        mDropTargetBar = dropTargetBar;
     }
 
     @Override
@@ -189,16 +199,20 @@
         }
     }
 
-	@Override
-    public final void onDragStart(DragSource source, Object info, int dragAction) {
-        mActive = supportsDrop(source, info);
+    @Override
+    public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+        mActive = supportsDrop(dragObject.dragSource, dragObject.dragInfo);
         mDrawable.setColorFilter(null);
         if (mCurrentColorAnim != null) {
             mCurrentColorAnim.cancel();
             mCurrentColorAnim = null;
         }
         setTextColor(mOriginalTextColor);
-        ((ViewGroup) getParent()).setVisibility(mActive ? View.VISIBLE : View.GONE);
+        (mHideParentOnDisable ? ((ViewGroup) getParent()) : this)
+                .setVisibility(mActive ? View.VISIBLE : View.GONE);
+
+        mAccessibleDrag = options.isAccessibleDrag;
+        setOnClickListener(mAccessibleDrag ? this : null);
     }
 
     @Override
@@ -206,16 +220,18 @@
         return supportsDrop(dragObject.dragSource, dragObject.dragInfo);
     }
 
-    protected abstract boolean supportsDrop(DragSource source, Object info);
+    protected abstract boolean supportsDrop(DragSource source, ItemInfo info);
 
     @Override
     public boolean isDropEnabled() {
-        return mActive;
+        return mActive && (mAccessibleDrag ||
+                mLauncher.getDragController().getDistanceDragged() >= mDragDistanceThreshold);
     }
 
     @Override
     public void onDragEnd() {
         mActive = false;
+        setOnClickListener(null);
     }
 
     /**
@@ -232,18 +248,19 @@
         final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
                 width, height);
         final float scale = (float) to.width() / from.width();
-        mSearchDropTargetBar.deferOnDragEnd();
+        mDropTargetBar.deferOnDragEnd();
 
         Runnable onAnimationEndRunnable = new Runnable() {
             @Override
             public void run() {
                 completeDrop(d);
-                mSearchDropTargetBar.onDragEnd();
+                mDropTargetBar.onDragEnd();
                 mLauncher.exitSpringLoadedDragModeDelayed(true, 0, null);
             }
         };
         dragLayer.animateView(d.dragView, from, to, scale, 1f, 1f, 0.1f, 0.1f,
-                DRAG_VIEW_DROP_DURATION, new DecelerateInterpolator(2),
+                mLauncher.getDragController().isExternalDrag() ? 1 : DRAG_VIEW_DROP_DURATION,
+                new DecelerateInterpolator(2),
                 new LinearInterpolator(), onAnimationEndRunnable,
                 DragLayer.ANIMATION_END_DISAPPEAR, null);
     }
@@ -298,18 +315,8 @@
     }
 
     @Override
-    public void getLocationInDragLayer(int[] loc) {
-        mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
-    }
-
-    public void enableAccessibleDrag(boolean enable) {
-        setOnClickListener(enable ? this : null);
-    }
-
-    @Override
     public void onClick(View v) {
-        LauncherAppState.getInstance().getAccessibilityDelegate()
-            .handleAccessibleDrop(this, null, null);
+        mLauncher.getAccessibilityDelegate().handleAccessibleDrop(this, null, null);
     }
 
     public int getTextColor() {
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 94e3e41..57fd0e7 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -18,7 +18,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -48,10 +47,16 @@
 import android.view.animation.DecelerateInterpolator;
 
 import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
-import com.android.launcher3.FolderIcon.FolderRingAnimator;
+import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
 import com.android.launcher3.accessibility.FolderAccessibilityHelper;
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.util.CellAndSpan;
+import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.ParcelableSparseArray;
 import com.android.launcher3.util.Thunk;
 
@@ -70,17 +75,23 @@
     private static final boolean LOGD = false;
 
     private Launcher mLauncher;
+    @ViewDebug.ExportedProperty(category = "launcher")
     @Thunk int mCellWidth;
+    @ViewDebug.ExportedProperty(category = "launcher")
     @Thunk int mCellHeight;
     private int mFixedCellWidth;
     private int mFixedCellHeight;
 
-    @Thunk int mCountX;
-    @Thunk int mCountY;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private int mCountX;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private int mCountY;
 
     private int mOriginalWidthGap;
     private int mOriginalHeightGap;
+    @ViewDebug.ExportedProperty(category = "launcher")
     @Thunk int mWidthGap;
+    @ViewDebug.ExportedProperty(category = "launcher")
     @Thunk int mHeightGap;
     private int mMaxGap;
     private boolean mDropPending = false;
@@ -92,18 +103,20 @@
     @Thunk final int[] mTmpPoint = new int[2];
     @Thunk final int[] mTempLocation = new int[2];
 
-    boolean[][] mOccupied;
-    boolean[][] mTmpOccupied;
+    private GridOccupancy mOccupied;
+    private GridOccupancy mTmpOccupied;
 
     private OnTouchListener mInterceptTouchListener;
     private StylusEventHelper mStylusEventHelper;
 
-    private ArrayList<FolderRingAnimator> mFolderOuterRings = new ArrayList<FolderRingAnimator>();
-    private int[] mFolderLeaveBehindCell = {-1, -1};
+    private ArrayList<FolderIcon.PreviewBackground> mFolderBackgrounds = new ArrayList<FolderIcon.PreviewBackground>();
+    FolderIcon.PreviewBackground mFolderLeaveBehind = new FolderIcon.PreviewBackground();
+    Paint mFolderBgPaint = new Paint();
 
     private float mBackgroundAlpha;
 
-    private static final int BACKGROUND_ACTIVATE_DURATION = 120;
+    private static final int BACKGROUND_ACTIVATE_DURATION =
+            FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? 120 : 0;
     private final TransitionDrawable mBackground;
 
     // These values allow a fixed measurement to be set on the CellLayout.
@@ -150,9 +163,6 @@
     private static final boolean DESTRUCTIVE_REORDER = false;
     private static final boolean DEBUG_VISUALIZE_OCCUPIED = false;
 
-    static final int LANDSCAPE = 0;
-    static final int PORTRAIT = 1;
-
     private static final float REORDER_PREVIEW_MAGNITUDE = 0.12f;
     private static final int REORDER_ANIMATION_DURATION = 150;
     @Thunk float mReorderPreviewAnimationMagnitude;
@@ -186,7 +196,7 @@
         // the user where a dragged item will land when dropped.
         setWillNotDraw(false);
         setClipToPadding(false);
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
 
         DeviceProfile grid = mLauncher.getDeviceProfile();
 
@@ -195,18 +205,25 @@
         mWidthGap = mOriginalWidthGap = 0;
         mHeightGap = mOriginalHeightGap = 0;
         mMaxGap = Integer.MAX_VALUE;
-        mCountX = (int) grid.inv.numColumns;
-        mCountY = (int) grid.inv.numRows;
-        mOccupied = new boolean[mCountX][mCountY];
-        mTmpOccupied = new boolean[mCountX][mCountY];
+
+        mCountX = grid.inv.numColumns;
+        mCountY = grid.inv.numRows;
+        mOccupied =  new GridOccupancy(mCountX, mCountY);
+        mTmpOccupied = new GridOccupancy(mCountX, mCountY);
+
         mPreviousReorderDirection[0] = INVALID_DIRECTION;
         mPreviousReorderDirection[1] = INVALID_DIRECTION;
 
+        mFolderLeaveBehind.delegateCellX = -1;
+        mFolderLeaveBehind.delegateCellY = -1;
+
         setAlwaysDrawnWithCacheEnabled(false);
         final Resources res = getResources();
         mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx;
 
-        mBackground = (TransitionDrawable) res.getDrawable(R.drawable.bg_screenpanel);
+        mBackground = (TransitionDrawable) res.getDrawable(
+                FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? R.drawable.bg_screenpanel
+                        : R.drawable.bg_celllayout);
         mBackground.setCallback(this);
         mBackground.setAlpha((int) (mBackgroundAlpha * 255));
 
@@ -219,6 +236,7 @@
         for (int i = 0; i < mDragOutlines.length; i++) {
             mDragOutlines[i] = new Rect(-1, -1, -1, -1);
         }
+        mDragOutlinePaint.setColor(getResources().getColor(R.color.outline_color));
 
         // When dragging things around the home screens, we show a green outline of
         // where the item will land. The outlines gradually fade out, leaving a trail
@@ -272,7 +290,7 @@
         mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
                 mCountX, mCountY);
 
-        mStylusEventHelper = new StylusEventHelper(this);
+        mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
 
         mTouchFeedbackView = new ClickShadowView(context);
         addView(mTouchFeedbackView);
@@ -334,7 +352,7 @@
         // enabled to allow rearranging the different home screens. So check what mode
         // the workspace is in, and only perform stylus button presses while in overview mode.
         if (mLauncher.mWorkspace.isInOverviewMode()
-                && mStylusEventHelper.checkAndPerformStylusEvent(ev)) {
+                && mStylusEventHelper.onMotionEvent(ev)) {
             return true;
         }
         return handled;
@@ -362,8 +380,8 @@
     public void setGridSize(int x, int y) {
         mCountX = x;
         mCountY = y;
-        mOccupied = new boolean[mCountX][mCountY];
-        mTmpOccupied = new boolean[mCountX][mCountY];
+        mOccupied = new GridOccupancy(mCountX, mCountY);
+        mTmpOccupied = new GridOccupancy(mCountX, mCountY);
         mTempRectStack.clear();
         mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
                 mCountX, mCountY);
@@ -390,7 +408,8 @@
             mTouchFeedbackView.animate().cancel();
         } else {
             if (mTouchFeedbackView.setBitmap(background)) {
-                mTouchFeedbackView.alignWithIconView(icon, mShortcutsAndWidgets);
+                mTouchFeedbackView.alignWithIconView(icon, mShortcutsAndWidgets,
+                        null /* clipAgainstView */);
                 mTouchFeedbackView.animateShadow();
             }
         }
@@ -400,7 +419,7 @@
         mIsDragTarget = false;
     }
 
-    boolean isDragTarget() {
+    public boolean isDragTarget() {
         return mIsDragTarget;
     }
 
@@ -420,10 +439,6 @@
         }
     }
 
-    public boolean getIsDragOverlapping() {
-        return mIsDragOverlapping;
-    }
-
     public void disableJailContent() {
         mJailContent = false;
     }
@@ -450,6 +465,10 @@
                 (ParcelableSparseArray) parcelable : new ParcelableSparseArray();
     }
 
+    public boolean getIsDragOverlapping() {
+        return mIsDragOverlapping;
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         if (!mIsDragTarget) {
@@ -469,12 +488,9 @@
         for (int i = 0; i < mDragOutlines.length; i++) {
             final float alpha = mDragOutlineAlphas[i];
             if (alpha > 0) {
-                final Rect r = mDragOutlines[i];
-                mTempRect.set(r);
-                Utilities.scaleRectAboutCenter(mTempRect, getChildrenScale());
                 final Bitmap b = (Bitmap) mDragOutlineAnims[i].getTag();
                 paint.setAlpha((int)(alpha + .5f));
-                canvas.drawBitmap(b, null, mTempRect, paint);
+                canvas.drawBitmap(b, null, mDragOutlines[i], paint);
             }
         }
 
@@ -484,7 +500,7 @@
             cd.setBounds(0, 0,  mCellWidth, mCellHeight);
             for (int i = 0; i < mCountX; i++) {
                 for (int j = 0; j < mCountY; j++) {
-                    if (mOccupied[i][j]) {
+                    if (mOccupied.cells[i][j]) {
                         cellToPoint(i, j, pt);
                         canvas.save();
                         canvas.translate(pt[0], pt[1]);
@@ -495,88 +511,67 @@
             }
         }
 
-        int previewOffset = FolderRingAnimator.sPreviewSize;
-
-        // The folder outer / inner ring image(s)
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        for (int i = 0; i < mFolderOuterRings.size(); i++) {
-            FolderRingAnimator fra = mFolderOuterRings.get(i);
-
-            Drawable d;
-            int width, height;
-            cellToPoint(fra.mCellX, fra.mCellY, mTempLocation);
-            View child = getChildAt(fra.mCellX, fra.mCellY);
-
-            if (child != null) {
-                int centerX = mTempLocation[0] + mCellWidth / 2;
-                int centerY = mTempLocation[1] + previewOffset / 2 +
-                        child.getPaddingTop() + grid.folderBackgroundOffset;
-
-                // Draw outer ring, if it exists
-                if (FolderIcon.HAS_OUTER_RING) {
-                    d = FolderRingAnimator.sSharedOuterRingDrawable;
-                    width = (int) (fra.getOuterRingSize() * getChildrenScale());
-                    height = width;
-                    canvas.save();
-                    canvas.translate(centerX - width / 2, centerY - height / 2);
-                    d.setBounds(0, 0, width, height);
-                    d.draw(canvas);
-                    canvas.restore();
-                }
-
-                // Draw inner ring
-                d = FolderRingAnimator.sSharedInnerRingDrawable;
-                width = (int) (fra.getInnerRingSize() * getChildrenScale());
-                height = width;
-                canvas.save();
-                canvas.translate(centerX - width / 2, centerY - width / 2);
-                d.setBounds(0, 0, width, height);
-                d.draw(canvas);
-                canvas.restore();
+        for (int i = 0; i < mFolderBackgrounds.size(); i++) {
+            FolderIcon.PreviewBackground bg = mFolderBackgrounds.get(i);
+            cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
+            canvas.save();
+            canvas.translate(mTempLocation[0], mTempLocation[1]);
+            bg.drawBackground(canvas, mFolderBgPaint);
+            if (!bg.isClipping) {
+                bg.drawBackgroundStroke(canvas, mFolderBgPaint);
             }
+            canvas.restore();
         }
 
-        if (mFolderLeaveBehindCell[0] >= 0 && mFolderLeaveBehindCell[1] >= 0) {
-            Drawable d = FolderIcon.sSharedFolderLeaveBehind;
-            int width = d.getIntrinsicWidth();
-            int height = d.getIntrinsicHeight();
+        if (mFolderLeaveBehind.delegateCellX >= 0 && mFolderLeaveBehind.delegateCellY >= 0) {
+            cellToPoint(mFolderLeaveBehind.delegateCellX,
+                    mFolderLeaveBehind.delegateCellY, mTempLocation);
+            canvas.save();
+            canvas.translate(mTempLocation[0], mTempLocation[1]);
+            mFolderLeaveBehind.drawLeaveBehind(canvas, mFolderBgPaint);
+            canvas.restore();
+        }
+    }
 
-            cellToPoint(mFolderLeaveBehindCell[0], mFolderLeaveBehindCell[1], mTempLocation);
-            View child = getChildAt(mFolderLeaveBehindCell[0], mFolderLeaveBehindCell[1]);
-            if (child != null) {
-                int centerX = mTempLocation[0] + mCellWidth / 2;
-                int centerY = mTempLocation[1] + previewOffset / 2 +
-                        child.getPaddingTop() + grid.folderBackgroundOffset;
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
 
+        for (int i = 0; i < mFolderBackgrounds.size(); i++) {
+            FolderIcon.PreviewBackground bg = mFolderBackgrounds.get(i);
+            if (bg.isClipping) {
+                cellToPoint(bg.delegateCellX, bg.delegateCellY, mTempLocation);
                 canvas.save();
-                canvas.translate(centerX - width / 2, centerY - width / 2);
-                d.setBounds(0, 0, width, height);
-                d.draw(canvas);
+                canvas.translate(mTempLocation[0], mTempLocation[1]);
+                bg.drawBackgroundStroke(canvas, mFolderBgPaint);
                 canvas.restore();
             }
         }
     }
 
-    public void showFolderAccept(FolderRingAnimator fra) {
-        mFolderOuterRings.add(fra);
+    public void addFolderBackground(FolderIcon.PreviewBackground bg) {
+        mFolderBackgrounds.add(bg);
     }
-
-    public void hideFolderAccept(FolderRingAnimator fra) {
-        if (mFolderOuterRings.contains(fra)) {
-            mFolderOuterRings.remove(fra);
-        }
-        invalidate();
+    public void removeFolderBackground(FolderIcon.PreviewBackground bg) {
+        mFolderBackgrounds.remove(bg);
     }
 
     public void setFolderLeaveBehindCell(int x, int y) {
-        mFolderLeaveBehindCell[0] = x;
-        mFolderLeaveBehindCell[1] = y;
+
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        View child = getChildAt(x, y);
+
+        mFolderLeaveBehind.setup(getResources().getDisplayMetrics(), grid, null,
+                child.getMeasuredWidth(), child.getPaddingTop());
+
+        mFolderLeaveBehind.delegateCellX = x;
+        mFolderLeaveBehind.delegateCellY = y;
         invalidate();
     }
 
     public void clearFolderLeaveBehind() {
-        mFolderLeaveBehindCell[0] = -1;
-        mFolderLeaveBehindCell[1] = -1;
+        mFolderLeaveBehind.delegateCellX = -1;
+        mFolderLeaveBehind.delegateCellY = -1;
         invalidate();
     }
 
@@ -589,7 +584,7 @@
         try {
             dispatchRestoreInstanceState(states);
         } catch (IllegalArgumentException ex) {
-            if (LauncherAppState.isDogfoodBuild()) {
+            if (ProviderConfig.IS_DOGFOOD_BUILD) {
                 throw ex;
             }
             // Mismatched viewId / viewType preventing restore. Skip restore on production builds.
@@ -666,14 +661,14 @@
 
     @Override
     public void removeAllViews() {
-        clearOccupiedCells();
+        mOccupied.clear();
         mShortcutsAndWidgets.removeAllViews();
     }
 
     @Override
     public void removeAllViewsInLayout() {
         if (mShortcutsAndWidgets.getChildCount() > 0) {
-            clearOccupiedCells();
+            mOccupied.clear();
             mShortcutsAndWidgets.removeAllViewsInLayout();
         }
     }
@@ -810,7 +805,7 @@
         return (float) Math.hypot(x - mTmpPoint[0], y - mTmpPoint[1]);
     }
 
-    int getCellWidth() {
+    public int getCellWidth() {
         return mCellWidth;
     }
 
@@ -904,14 +899,26 @@
         if (!isFullscreen) {
             left += (int) Math.ceil(getUnusedHorizontalSpace() / 2f);
         }
+        int right = r - l - getPaddingRight();
+        if (!isFullscreen) {
+            right -= (int) Math.ceil(getUnusedHorizontalSpace() / 2f);
+        }
+
         int top = getPaddingTop();
+        int bottom = b - t - getPaddingBottom();
 
         mTouchFeedbackView.layout(left, top,
                 left + mTouchFeedbackView.getMeasuredWidth(),
                 top + mTouchFeedbackView.getMeasuredHeight());
-        mShortcutsAndWidgets.layout(left, top,
-                left + r - l,
-                top + b - t);
+        mShortcutsAndWidgets.layout(left, top, right, bottom);
+
+        // Expand the background drawing bounds by the padding baked into the background drawable
+        mBackground.getPadding(mTempRect);
+        mBackground.setBounds(
+                left - mTempRect.left,
+                top - mTempRect.top,
+                right + mTempRect.right,
+                bottom + mTempRect.bottom);
     }
 
     /**
@@ -924,16 +931,6 @@
     }
 
     @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-
-        // Expand the background drawing bounds by the padding baked into the background drawable
-        mBackground.getPadding(mTempRect);
-        mBackground.setBounds(-mTempRect.left, -mTempRect.top,
-                w + mTempRect.right, h + mTempRect.bottom);
-    }
-
-    @Override
     protected void setChildrenDrawingCacheEnabled(boolean enabled) {
         mShortcutsAndWidgets.setChildrenDrawingCacheEnabled(enabled);
     }
@@ -974,10 +971,6 @@
     public boolean animateChildToPosition(final View child, int cellX, int cellY, int duration,
             int delay, boolean permanent, boolean adjustOccupied) {
         ShortcutAndWidgetContainer clc = getShortcutsAndWidgets();
-        boolean[][] occupied = mOccupied;
-        if (!permanent) {
-            occupied = mTmpOccupied;
-        }
 
         if (clc.indexOfChild(child) != -1) {
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
@@ -992,8 +985,9 @@
             final int oldX = lp.x;
             final int oldY = lp.y;
             if (adjustOccupied) {
-                occupied[lp.cellX][lp.cellY] = false;
-                occupied[cellX][cellY] = true;
+                GridOccupancy occupied = permanent ? mOccupied : mTmpOccupied;
+                occupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
+                occupied.markCells(cellX, cellY, lp.cellHSpan, lp.cellVSpan, true);
             }
             lp.isLockedToGrid = true;
             if (permanent) {
@@ -1055,68 +1049,73 @@
         return false;
     }
 
-    void visualizeDropLocation(View v, Bitmap dragOutline, int cellX, int cellY, int spanX,
-            int spanY, boolean resize, DropTarget.DragObject dragObject) {
+    void visualizeDropLocation(View v, DragPreviewProvider outlineProvider, int cellX, int cellY,
+            int spanX, int spanY, boolean resize, DropTarget.DragObject dragObject) {
         final int oldDragCellX = mDragCell[0];
         final int oldDragCellY = mDragCell[1];
 
-        if (dragOutline == null && v == null) {
+        if (outlineProvider == null || outlineProvider.gerenatedDragOutline == null) {
             return;
         }
 
+        Bitmap dragOutline = outlineProvider.gerenatedDragOutline;
         if (cellX != oldDragCellX || cellY != oldDragCellY) {
             Point dragOffset = dragObject.dragView.getDragVisualizeOffset();
             Rect dragRegion = dragObject.dragView.getDragRegion();
 
             mDragCell[0] = cellX;
             mDragCell[1] = cellY;
-            // Find the top left corner of the rect the object will occupy
-            final int[] topLeft = mTmpPoint;
-            cellToPoint(cellX, cellY, topLeft);
 
-            int left = topLeft[0];
-            int top = topLeft[1];
-
-            if (v != null && dragOffset == null) {
-                // When drawing the drag outline, it did not account for margin offsets
-                // added by the view's parent.
-                MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
-                left += lp.leftMargin;
-                top += lp.topMargin;
-
-                // Offsets due to the size difference between the View and the dragOutline.
-                // There is a size difference to account for the outer blur, which may lie
-                // outside the bounds of the view.
-                top += (v.getHeight() - dragOutline.getHeight()) / 2;
-                // We center about the x axis
-                left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
-                        - dragOutline.getWidth()) / 2;
-            } else {
-                if (dragOffset != null && dragRegion != null) {
-                    // Center the drag region *horizontally* in the cell and apply a drag
-                    // outline offset
-                    left += dragOffset.x + ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
-                             - dragRegion.width()) / 2;
-                    int cHeight = getShortcutsAndWidgets().getCellContentHeight();
-                    int cellPaddingY = (int) Math.max(0, ((mCellHeight - cHeight) / 2f));
-                    top += dragOffset.y + cellPaddingY;
-                } else {
-                    // Center the drag outline in the cell
-                    left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
-                            - dragOutline.getWidth()) / 2;
-                    top += ((mCellHeight * spanY) + ((spanY - 1) * mHeightGap)
-                            - dragOutline.getHeight()) / 2;
-                }
-            }
             final int oldIndex = mDragOutlineCurrent;
             mDragOutlineAnims[oldIndex].animateOut();
             mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
             Rect r = mDragOutlines[mDragOutlineCurrent];
-            r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight());
+
             if (resize) {
                 cellToRect(cellX, cellY, spanX, spanY, r);
+            } else {
+                // Find the top left corner of the rect the object will occupy
+                final int[] topLeft = mTmpPoint;
+                cellToPoint(cellX, cellY, topLeft);
+
+                int left = topLeft[0];
+                int top = topLeft[1];
+
+                if (v != null && dragOffset == null) {
+                    // When drawing the drag outline, it did not account for margin offsets
+                    // added by the view's parent.
+                    MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
+                    left += lp.leftMargin;
+                    top += lp.topMargin;
+
+                    // Offsets due to the size difference between the View and the dragOutline.
+                    // There is a size difference to account for the outer blur, which may lie
+                    // outside the bounds of the view.
+                    top += (v.getHeight() - dragOutline.getHeight()) / 2;
+                    // We center about the x axis
+                    left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
+                            - dragOutline.getWidth()) / 2;
+                } else {
+                    if (dragOffset != null && dragRegion != null) {
+                        // Center the drag region *horizontally* in the cell and apply a drag
+                        // outline offset
+                        left += dragOffset.x + ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
+                                - dragRegion.width()) / 2;
+                        int cHeight = getShortcutsAndWidgets().getCellContentHeight();
+                        int cellPaddingY = (int) Math.max(0, ((mCellHeight - cHeight) / 2f));
+                        top += dragOffset.y + cellPaddingY;
+                    } else {
+                        // Center the drag outline in the cell
+                        left += ((mCellWidth * spanX) + ((spanX - 1) * mWidthGap)
+                                - dragOutline.getWidth()) / 2;
+                        top += ((mCellHeight * spanY) + ((spanY - 1) * mHeightGap)
+                                - dragOutline.getHeight()) / 2;
+                    }
+                }
+                r.set(left, top, left + dragOutline.getWidth(), top + dragOutline.getHeight());
             }
 
+            Utilities.scaleRectAboutCenter(r, getChildrenScale());
             mDragOutlineAnims[mDragOutlineCurrent].setTag(dragOutline);
             mDragOutlineAnims[mDragOutlineCurrent].animateIn();
 
@@ -1146,23 +1145,6 @@
      *
      * @param pixelX The X location at which you want to search for a vacant area.
      * @param pixelY The Y location at which you want to search for a vacant area.
-     * @param spanX Horizontal span of the object.
-     * @param spanY Vertical span of the object.
-     * @param result Array in which to place the result, or null (in which case a new array will
-     *        be allocated)
-     * @return The X, Y cell of a vacant area that can contain this object,
-     *         nearest the requested location.
-     */
-    int[] findNearestVacantArea(int pixelX, int pixelY, int spanX, int spanY, int[] result) {
-        return findNearestVacantArea(pixelX, pixelY, spanX, spanY, spanX, spanY, result, null);
-    }
-
-    /**
-     * Find a vacant area that will fit the given bounds nearest the requested
-     * cell location. Uses Euclidean distance to score multiple vacant areas.
-     *
-     * @param pixelX The X location at which you want to search for a vacant area.
-     * @param pixelY The Y location at which you want to search for a vacant area.
      * @param minSpanX The minimum horizontal span required
      * @param minSpanY The minimum vertical span required
      * @param spanX Horizontal span of the object.
@@ -1242,7 +1224,7 @@
                     // First, let's see if this thing fits anywhere
                     for (int i = 0; i < minSpanX; i++) {
                         for (int j = 0; j < minSpanY; j++) {
-                            if (mOccupied[x + i][y + j]) {
+                            if (mOccupied.cells[x + i][y + j]) {
                                 continue inner;
                             }
                         }
@@ -1259,7 +1241,7 @@
                     while (!(hitMaxX && hitMaxY)) {
                         if (incX && !hitMaxX) {
                             for (int j = 0; j < ySize; j++) {
-                                if (x + xSize > countX -1 || mOccupied[x + xSize][y + j]) {
+                                if (x + xSize > countX -1 || mOccupied.cells[x + xSize][y + j]) {
                                     // We can't move out horizontally
                                     hitMaxX = true;
                                 }
@@ -1269,7 +1251,7 @@
                             }
                         } else if (!hitMaxY) {
                             for (int i = 0; i < xSize; i++) {
-                                if (y + ySize > countY - 1 || mOccupied[x + i][y + ySize]) {
+                                if (y + ySize > countY - 1 || mOccupied.cells[x + i][y + ySize]) {
                                     // We can't move out vertically
                                     hitMaxY = true;
                                 }
@@ -1327,7 +1309,7 @@
         return bestXY;
     }
 
-     /**
+    /**
      * Find a vacant area that will fit the given bounds nearest the requested
      * cell location, and will also weigh in a suggested direction vector of the
      * desired location. This method computers distance based on unit grid distances,
@@ -1338,9 +1320,7 @@
      * @param spanX Horizontal span of the object.
      * @param spanY Vertical span of the object.
      * @param direction The favored direction in which the views should move from x, y
-     * @param exactDirectionOnly If this parameter is true, then only solutions where the direction
-     *        matches exactly. Otherwise we find the best matching direction.
-     * @param occoupied The array which represents which cells in the CellLayout are occupied
+     * @param occupied The array which represents which cells in the CellLayout are occupied
      * @param blockOccupied The array which represents which cells in the specified block (cellX,
      *        cellY, spanX, spanY) are occupied. This is used when try to move a group of views.
      * @param result Array in which to place the result, or null (in which case a new array will
@@ -1377,12 +1357,9 @@
                 // and that passed in.
                 int curDirectionScore = direction[0] * curDirection[0] +
                         direction[1] * curDirection[1];
-                boolean exactDirectionOnly = false;
-                boolean directionMatches = direction[0] == curDirection[0] &&
-                        direction[0] == curDirection[0];
-                if ((directionMatches || !exactDirectionOnly) &&
-                        Float.compare(distance,  bestDistance) < 0 || (Float.compare(distance,
-                        bestDistance) == 0 && curDirectionScore > bestDirectionScore)) {
+                if (Float.compare(distance,  bestDistance) < 0 ||
+                        (Float.compare(distance, bestDistance) == 0
+                                && curDirectionScore > bestDirectionScore)) {
                     bestDistance = distance;
                     bestDirectionScore = curDirectionScore;
                     bestXY[0] = x;
@@ -1403,17 +1380,18 @@
             int[] direction, ItemConfiguration currentState) {
         CellAndSpan c = currentState.map.get(v);
         boolean success = false;
-        markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
-        markCellsForRect(rectOccupiedByPotentialDrop, mTmpOccupied, true);
+        mTmpOccupied.markCells(c, false);
+        mTmpOccupied.markCells(rectOccupiedByPotentialDrop, true);
 
-        findNearestArea(c.x, c.y, c.spanX, c.spanY, direction, mTmpOccupied, null, mTempLocation);
+        findNearestArea(c.cellX, c.cellY, c.spanX, c.spanY, direction,
+                mTmpOccupied.cells, null, mTempLocation);
 
         if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
-            c.x = mTempLocation[0];
-            c.y = mTempLocation[1];
+            c.cellX = mTempLocation[0];
+            c.cellY = mTempLocation[1];
             success = true;
         }
-        markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
+        mTmpOccupied.markCells(c, true);
         return success;
     }
 
@@ -1424,10 +1402,10 @@
      * precise version of a bounding box.
      */
     private class ViewCluster {
-        final static int LEFT = 0;
-        final static int TOP = 1;
-        final static int RIGHT = 2;
-        final static int BOTTOM = 3;
+        final static int LEFT = 1 << 0;
+        final static int TOP = 1 << 1;
+        final static int RIGHT = 1 << 2;
+        final static int BOTTOM = 1 << 3;
 
         ArrayList<View> views;
         ItemConfiguration config;
@@ -1437,7 +1415,8 @@
         int[] rightEdge = new int[mCountY];
         int[] topEdge = new int[mCountX];
         int[] bottomEdge = new int[mCountX];
-        boolean leftEdgeDirty, rightEdgeDirty, topEdgeDirty, bottomEdgeDirty, boundingRectDirty;
+        int dirtyEdges;
+        boolean boundingRectDirty;
 
         @SuppressWarnings("unchecked")
         public ViewCluster(ArrayList<View> views, ItemConfiguration config) {
@@ -1455,47 +1434,44 @@
                 leftEdge[i] = -1;
                 rightEdge[i] = -1;
             }
-            leftEdgeDirty = true;
-            rightEdgeDirty = true;
-            bottomEdgeDirty = true;
-            topEdgeDirty = true;
+            dirtyEdges = LEFT | TOP | RIGHT | BOTTOM;
             boundingRectDirty = true;
         }
 
-        void computeEdge(int which, int[] edge) {
+        void computeEdge(int which) {
             int count = views.size();
             for (int i = 0; i < count; i++) {
                 CellAndSpan cs = config.map.get(views.get(i));
                 switch (which) {
                     case LEFT:
-                        int left = cs.x;
-                        for (int j = cs.y; j < cs.y + cs.spanY; j++) {
-                            if (left < edge[j] || edge[j] < 0) {
-                                edge[j] = left;
+                        int left = cs.cellX;
+                        for (int j = cs.cellY; j < cs.cellY + cs.spanY; j++) {
+                            if (left < leftEdge[j] || leftEdge[j] < 0) {
+                                leftEdge[j] = left;
                             }
                         }
                         break;
                     case RIGHT:
-                        int right = cs.x + cs.spanX;
-                        for (int j = cs.y; j < cs.y + cs.spanY; j++) {
-                            if (right > edge[j]) {
-                                edge[j] = right;
+                        int right = cs.cellX + cs.spanX;
+                        for (int j = cs.cellY; j < cs.cellY + cs.spanY; j++) {
+                            if (right > rightEdge[j]) {
+                                rightEdge[j] = right;
                             }
                         }
                         break;
                     case TOP:
-                        int top = cs.y;
-                        for (int j = cs.x; j < cs.x + cs.spanX; j++) {
-                            if (top < edge[j] || edge[j] < 0) {
-                                edge[j] = top;
+                        int top = cs.cellY;
+                        for (int j = cs.cellX; j < cs.cellX + cs.spanX; j++) {
+                            if (top < topEdge[j] || topEdge[j] < 0) {
+                                topEdge[j] = top;
                             }
                         }
                         break;
                     case BOTTOM:
-                        int bottom = cs.y + cs.spanY;
-                        for (int j = cs.x; j < cs.x + cs.spanX; j++) {
-                            if (bottom > edge[j]) {
-                                edge[j] = bottom;
+                        int bottom = cs.cellY + cs.spanY;
+                        for (int j = cs.cellX; j < cs.cellX + cs.spanX; j++) {
+                            if (bottom > bottomEdge[j]) {
+                                bottomEdge[j] = bottom;
                             }
                         }
                         break;
@@ -1506,33 +1482,36 @@
         boolean isViewTouchingEdge(View v, int whichEdge) {
             CellAndSpan cs = config.map.get(v);
 
-            int[] edge = getEdge(whichEdge);
+            if ((dirtyEdges & whichEdge) == whichEdge) {
+                computeEdge(whichEdge);
+                dirtyEdges &= ~whichEdge;
+            }
 
             switch (whichEdge) {
                 case LEFT:
-                    for (int i = cs.y; i < cs.y + cs.spanY; i++) {
-                        if (edge[i] == cs.x + cs.spanX) {
+                    for (int i = cs.cellY; i < cs.cellY + cs.spanY; i++) {
+                        if (leftEdge[i] == cs.cellX + cs.spanX) {
                             return true;
                         }
                     }
                     break;
                 case RIGHT:
-                    for (int i = cs.y; i < cs.y + cs.spanY; i++) {
-                        if (edge[i] == cs.x) {
+                    for (int i = cs.cellY; i < cs.cellY + cs.spanY; i++) {
+                        if (rightEdge[i] == cs.cellX) {
                             return true;
                         }
                     }
                     break;
                 case TOP:
-                    for (int i = cs.x; i < cs.x + cs.spanX; i++) {
-                        if (edge[i] == cs.y + cs.spanY) {
+                    for (int i = cs.cellX; i < cs.cellX + cs.spanX; i++) {
+                        if (topEdge[i] == cs.cellY + cs.spanY) {
                             return true;
                         }
                     }
                     break;
                 case BOTTOM:
-                    for (int i = cs.x; i < cs.x + cs.spanX; i++) {
-                        if (edge[i] == cs.y) {
+                    for (int i = cs.cellX; i < cs.cellX + cs.spanX; i++) {
+                        if (bottomEdge[i] == cs.cellY) {
                             return true;
                         }
                     }
@@ -1546,17 +1525,17 @@
                 CellAndSpan c = config.map.get(v);
                 switch (whichEdge) {
                     case LEFT:
-                        c.x -= delta;
+                        c.cellX -= delta;
                         break;
                     case RIGHT:
-                        c.x += delta;
+                        c.cellX += delta;
                         break;
                     case TOP:
-                        c.y -= delta;
+                        c.cellY -= delta;
                         break;
                     case BOTTOM:
                     default:
-                        c.y += delta;
+                        c.cellY += delta;
                         break;
                 }
             }
@@ -1570,62 +1549,11 @@
 
         public Rect getBoundingRect() {
             if (boundingRectDirty) {
-                boolean first = true;
-                for (View v: views) {
-                    CellAndSpan c = config.map.get(v);
-                    if (first) {
-                        boundingRect.set(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
-                        first = false;
-                    } else {
-                        boundingRect.union(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
-                    }
-                }
+                config.getBoundingRectForViews(views, boundingRect);
             }
             return boundingRect;
         }
 
-        public int[] getEdge(int which) {
-            switch (which) {
-                case LEFT:
-                    return getLeftEdge();
-                case RIGHT:
-                    return getRightEdge();
-                case TOP:
-                    return getTopEdge();
-                case BOTTOM:
-                default:
-                    return getBottomEdge();
-            }
-        }
-
-        public int[] getLeftEdge() {
-            if (leftEdgeDirty) {
-                computeEdge(LEFT, leftEdge);
-            }
-            return leftEdge;
-        }
-
-        public int[] getRightEdge() {
-            if (rightEdgeDirty) {
-                computeEdge(RIGHT, rightEdge);
-            }
-            return rightEdge;
-        }
-
-        public int[] getTopEdge() {
-            if (topEdgeDirty) {
-                computeEdge(TOP, topEdge);
-            }
-            return topEdge;
-        }
-
-        public int[] getBottomEdge() {
-            if (bottomEdgeDirty) {
-                computeEdge(BOTTOM, bottomEdge);
-            }
-            return bottomEdge;
-        }
-
         PositionComparator comparator = new PositionComparator();
         class PositionComparator implements Comparator<View> {
             int whichEdge = 0;
@@ -1634,14 +1562,14 @@
                 CellAndSpan r = config.map.get(right);
                 switch (whichEdge) {
                     case LEFT:
-                        return (r.x + r.spanX) - (l.x + l.spanX);
+                        return (r.cellX + r.spanX) - (l.cellX + l.spanX);
                     case RIGHT:
-                        return l.x - r.x;
+                        return l.cellX - r.cellX;
                     case TOP:
-                        return (r.y + r.spanY) - (l.y + l.spanY);
+                        return (r.cellY + r.spanY) - (l.cellY + l.spanY);
                     case BOTTOM:
                     default:
-                        return l.y - r.y;
+                        return l.cellY - r.cellY;
                 }
             }
         }
@@ -1685,7 +1613,7 @@
         // Mark the occupied state as false for the group of views we want to move.
         for (View v: views) {
             CellAndSpan c = currentState.map.get(v);
-            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
+            mTmpOccupied.markCells(c, false);
         }
 
         // We save the current configuration -- if we fail to find a solution we will revert
@@ -1715,7 +1643,7 @@
                         CellAndSpan c = currentState.map.get(v);
 
                         // Adding view to cluster, mark it as not occupied.
-                        markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
+                        mTmpOccupied.markCells(c, false);
                     }
                 }
             }
@@ -1741,7 +1669,7 @@
         // In either case, we set the occupied array as marked for the location of the views
         for (View v: cluster.views) {
             CellAndSpan c = currentState.map.get(v);
-            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
+            mTmpOccupied.markCells(c, true);
         }
 
         return foundSolution;
@@ -1752,37 +1680,31 @@
         if (views.size() == 0) return true;
 
         boolean success = false;
-        Rect boundingRect = null;
+        Rect boundingRect = new Rect();
         // We construct a rect which represents the entire group of views passed in
-        for (View v: views) {
-            CellAndSpan c = currentState.map.get(v);
-            if (boundingRect == null) {
-                boundingRect = new Rect(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
-            } else {
-                boundingRect.union(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
-            }
-        }
+        currentState.getBoundingRectForViews(views, boundingRect);
 
         // Mark the occupied state as false for the group of views we want to move.
         for (View v: views) {
             CellAndSpan c = currentState.map.get(v);
-            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, false);
+            mTmpOccupied.markCells(c, false);
         }
 
-        boolean[][] blockOccupied = new boolean[boundingRect.width()][boundingRect.height()];
+        GridOccupancy blockOccupied = new GridOccupancy(boundingRect.width(), boundingRect.height());
         int top = boundingRect.top;
         int left = boundingRect.left;
         // We mark more precisely which parts of the bounding rect are truly occupied, allowing
         // for interlocking.
         for (View v: views) {
             CellAndSpan c = currentState.map.get(v);
-            markCellsForView(c.x - left, c.y - top, c.spanX, c.spanY, blockOccupied, true);
+            blockOccupied.markCells(c.cellX - left, c.cellY - top, c.spanX, c.spanY, true);
         }
 
-        markCellsForRect(rectOccupiedByPotentialDrop, mTmpOccupied, true);
+        mTmpOccupied.markCells(rectOccupiedByPotentialDrop, true);
 
         findNearestArea(boundingRect.left, boundingRect.top, boundingRect.width(),
-                boundingRect.height(), direction, mTmpOccupied, blockOccupied, mTempLocation);
+                boundingRect.height(), direction,
+                mTmpOccupied.cells, blockOccupied.cells, mTempLocation);
 
         // If we successfuly found a location by pushing the block of views, we commit it
         if (mTempLocation[0] >= 0 && mTempLocation[1] >= 0) {
@@ -1790,8 +1712,8 @@
             int deltaY = mTempLocation[1] - boundingRect.top;
             for (View v: views) {
                 CellAndSpan c = currentState.map.get(v);
-                c.x += deltaX;
-                c.y += deltaY;
+                c.cellX += deltaX;
+                c.cellY += deltaY;
             }
             success = true;
         }
@@ -1799,15 +1721,11 @@
         // In either case, we set the occupied array as marked for the location of the views
         for (View v: views) {
             CellAndSpan c = currentState.map.get(v);
-            markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
+            mTmpOccupied.markCells(c, true);
         }
         return success;
     }
 
-    private void markCellsForRect(Rect r, boolean[][] occupied, boolean value) {
-        markCellsForView(r.left, r.top, r.width(), r.height(), occupied, value);
-    }
-
     // This method tries to find a reordering solution which satisfies the push mechanic by trying
     // to push items in each of the cardinal directions, in an order based on the direction vector
     // passed.
@@ -1917,8 +1835,8 @@
         if (ignoreView != null) {
             CellAndSpan c = solution.map.get(ignoreView);
             if (c != null) {
-                c.x = cellX;
-                c.y = cellY;
+                c.cellX = cellX;
+                c.cellY = cellY;
             }
         }
         Rect r0 = new Rect(cellX, cellY, cellX + spanX, cellY + spanY);
@@ -1927,7 +1845,7 @@
             if (child == ignoreView) continue;
             CellAndSpan c = solution.map.get(child);
             LayoutParams lp = (LayoutParams) child.getLayoutParams();
-            r1.set(c.x, c.y, c.x + c.spanX, c.y + c.spanY);
+            r1.set(c.cellX, c.cellY, c.cellX + c.spanX, c.cellY + c.spanY);
             if (Rect.intersects(r0, r1)) {
                 if (!lp.canReorder) {
                     return false;
@@ -1978,14 +1896,6 @@
         }
     }
 
-    private void copyOccupiedArray(boolean[][] occupied) {
-        for (int i = 0; i < mCountX; i++) {
-            for (int j = 0; j < mCountY; j++) {
-                occupied[i][j] = mOccupied[i][j];
-            }
-        }
-    }
-
     private ItemConfiguration findReorderSolution(int pixelX, int pixelY, int minSpanX, int minSpanY,
             int spanX, int spanY, int[] direction, View dragView, boolean decX,
             ItemConfiguration solution) {
@@ -1993,7 +1903,7 @@
         copyCurrentStateToSolution(solution, false);
         // Copy the current occupied array into the temporary occupied array. This array will be
         // manipulated as necessary to find a solution.
-        copyOccupiedArray(mTmpOccupied);
+        mOccupied.copyTo(mTmpOccupied);
 
         // We find the nearest cell into which we would place the dragged item, assuming there's
         // nothing in its way.
@@ -2019,10 +1929,10 @@
             solution.isSolution = false;
         } else {
             solution.isSolution = true;
-            solution.dragViewX = result[0];
-            solution.dragViewY = result[1];
-            solution.dragViewSpanX = spanX;
-            solution.dragViewSpanY = spanY;
+            solution.cellX = result[0];
+            solution.cellY = result[1];
+            solution.spanX = spanX;
+            solution.spanY = spanY;
         }
         return solution;
     }
@@ -2043,11 +1953,7 @@
     }
 
     private void copySolutionToTempState(ItemConfiguration solution, View dragView) {
-        for (int i = 0; i < mCountX; i++) {
-            for (int j = 0; j < mCountY; j++) {
-                mTmpOccupied[i][j] = false;
-            }
-        }
+        mTmpOccupied.clear();
 
         int childCount = mShortcutsAndWidgets.getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -2056,26 +1962,21 @@
             LayoutParams lp = (LayoutParams) child.getLayoutParams();
             CellAndSpan c = solution.map.get(child);
             if (c != null) {
-                lp.tmpCellX = c.x;
-                lp.tmpCellY = c.y;
+                lp.tmpCellX = c.cellX;
+                lp.tmpCellY = c.cellY;
                 lp.cellHSpan = c.spanX;
                 lp.cellVSpan = c.spanY;
-                markCellsForView(c.x, c.y, c.spanX, c.spanY, mTmpOccupied, true);
+                mTmpOccupied.markCells(c, true);
             }
         }
-        markCellsForView(solution.dragViewX, solution.dragViewY, solution.dragViewSpanX,
-                solution.dragViewSpanY, mTmpOccupied, true);
+        mTmpOccupied.markCells(solution, true);
     }
 
     private void animateItemsToSolution(ItemConfiguration solution, View dragView, boolean
             commitDragView) {
 
-        boolean[][] occupied = DESTRUCTIVE_REORDER ? mOccupied : mTmpOccupied;
-        for (int i = 0; i < mCountX; i++) {
-            for (int j = 0; j < mCountY; j++) {
-                occupied[i][j] = false;
-            }
-        }
+        GridOccupancy occupied = DESTRUCTIVE_REORDER ? mOccupied : mTmpOccupied;
+        occupied.clear();
 
         int childCount = mShortcutsAndWidgets.getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -2083,14 +1984,13 @@
             if (child == dragView) continue;
             CellAndSpan c = solution.map.get(child);
             if (c != null) {
-                animateChildToPosition(child, c.x, c.y, REORDER_ANIMATION_DURATION, 0,
+                animateChildToPosition(child, c.cellX, c.cellY, REORDER_ANIMATION_DURATION, 0,
                         DESTRUCTIVE_REORDER, false);
-                markCellsForView(c.x, c.y, c.spanX, c.spanY, occupied, true);
+                occupied.markCells(c, true);
             }
         }
         if (commitDragView) {
-            markCellsForView(solution.dragViewX, solution.dragViewY, solution.dragViewSpanX,
-                    solution.dragViewSpanY, occupied, true);
+            occupied.markCells(solution, true);
         }
     }
 
@@ -2109,7 +2009,7 @@
             LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (c != null && !skip) {
                 ReorderPreviewAnimation rha = new ReorderPreviewAnimation(child, mode, lp.cellX,
-                        lp.cellY, c.x, c.y, c.spanX, c.spanY);
+                        lp.cellY, c.cellX, c.cellY, c.spanX, c.spanY);
                 rha.animate();
             }
         }
@@ -2234,17 +2134,14 @@
                 a.cancel();
             }
 
-            AnimatorSet s = LauncherAnimUtils.createAnimatorSet();
-            a = s;
-            s.playTogether(
-                LauncherAnimUtils.ofFloat(child, "scaleX", getChildrenScale()),
-                LauncherAnimUtils.ofFloat(child, "scaleY", getChildrenScale()),
-                LauncherAnimUtils.ofFloat(child, "translationX", 0f),
-                LauncherAnimUtils.ofFloat(child, "translationY", 0f)
-            );
-            s.setDuration(REORDER_ANIMATION_DURATION);
-            s.setInterpolator(new android.view.animation.DecelerateInterpolator(1.5f));
-            s.start();
+            a = new LauncherViewPropertyAnimator(child)
+                .scaleX(getChildrenScale())
+                .scaleY(getChildrenScale())
+                .translationX(0)
+                .translationY(0)
+                .setDuration(REORDER_ANIMATION_DURATION);
+            a.setInterpolator(new android.view.animation.DecelerateInterpolator(1.5f));
+            a.start();
         }
     }
 
@@ -2256,11 +2153,16 @@
     }
 
     private void commitTempPlacement() {
-        for (int i = 0; i < mCountX; i++) {
-            for (int j = 0; j < mCountY; j++) {
-                mOccupied[i][j] = mTmpOccupied[i][j];
-            }
+        mTmpOccupied.copyTo(mOccupied);
+
+        long screenId = mLauncher.getWorkspace().getIdForScreen(this);
+        int container = Favorites.CONTAINER_DESKTOP;
+
+        if (mLauncher.isHotseatLayout(this)) {
+            screenId = -1;
+            container = Favorites.CONTAINER_HOTSEAT;
         }
+
         int childCount = mShortcutsAndWidgets.getChildCount();
         for (int i = 0; i < childCount; i++) {
             View child = mShortcutsAndWidgets.getChildAt(i);
@@ -2269,17 +2171,21 @@
             // We do a null check here because the item info can be null in the case of the
             // AllApps button in the hotseat.
             if (info != null) {
-                if (info.cellX != lp.tmpCellX || info.cellY != lp.tmpCellY ||
-                        info.spanX != lp.cellHSpan || info.spanY != lp.cellVSpan) {
-                    info.requiresDbUpdate = true;
-                }
+                final boolean requiresDbUpdate = (info.cellX != lp.tmpCellX
+                        || info.cellY != lp.tmpCellY || info.spanX != lp.cellHSpan
+                        || info.spanY != lp.cellVSpan);
+
                 info.cellX = lp.cellX = lp.tmpCellX;
                 info.cellY = lp.cellY = lp.tmpCellY;
                 info.spanX = lp.cellHSpan;
                 info.spanY = lp.cellVSpan;
+
+                if (requiresDbUpdate) {
+                    LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId,
+                            info.cellX, info.cellY, info.spanX, info.spanY);
+                }
             }
         }
-        mLauncher.getWorkspace().updateItemLocationsInDatabase(this);
     }
 
     private void setUseTempCoords(boolean useTempCoords) {
@@ -2298,10 +2204,10 @@
                 resultSpan);
         if (result[0] >= 0 && result[1] >= 0) {
             copyCurrentStateToSolution(solution, false);
-            solution.dragViewX = result[0];
-            solution.dragViewY = result[1];
-            solution.dragViewSpanX = resultSpan[0];
-            solution.dragViewSpanY = resultSpan[1];
+            solution.cellX = result[0];
+            solution.cellY = result[1];
+            solution.spanX = resultSpan[0];
+            solution.spanY = resultSpan[1];
             solution.isSolution = true;
         } else {
             solution.isSolution = false;
@@ -2489,10 +2395,10 @@
             if (finalSolution != null) {
                 beginOrAdjustReorderPreviewAnimations(finalSolution, dragView, 0,
                         ReorderPreviewAnimation.MODE_HINT);
-                result[0] = finalSolution.dragViewX;
-                result[1] = finalSolution.dragViewY;
-                resultSpan[0] = finalSolution.dragViewSpanX;
-                resultSpan[1] = finalSolution.dragViewSpanY;
+                result[0] = finalSolution.cellX;
+                result[1] = finalSolution.cellY;
+                resultSpan[0] = finalSolution.spanX;
+                resultSpan[1] = finalSolution.spanY;
             } else {
                 result[0] = result[1] = resultSpan[0] = resultSpan[1] = -1;
             }
@@ -2505,10 +2411,10 @@
         }
 
         if (finalSolution != null) {
-            result[0] = finalSolution.dragViewX;
-            result[1] = finalSolution.dragViewY;
-            resultSpan[0] = finalSolution.dragViewSpanX;
-            resultSpan[1] = finalSolution.dragViewSpanY;
+            result[0] = finalSolution.cellX;
+            result[1] = finalSolution.cellY;
+            resultSpan[0] = finalSolution.spanX;
+            resultSpan[1] = finalSolution.spanY;
 
             // If we're just testing for a possible location (MODE_ACCEPT_DROP), we don't bother
             // committing anything or animating anything as we just want to determine if a solution
@@ -2550,25 +2456,24 @@
         return mItemPlacementDirty;
     }
 
-    @Thunk class ItemConfiguration {
+    private static class ItemConfiguration extends CellAndSpan {
         HashMap<View, CellAndSpan> map = new HashMap<View, CellAndSpan>();
         private HashMap<View, CellAndSpan> savedMap = new HashMap<View, CellAndSpan>();
         ArrayList<View> sortedViews = new ArrayList<View>();
         ArrayList<View> intersectingViews;
         boolean isSolution = false;
-        int dragViewX, dragViewY, dragViewSpanX, dragViewSpanY;
 
         void save() {
             // Copy current state into savedMap
             for (View v: map.keySet()) {
-                map.get(v).copy(savedMap.get(v));
+                savedMap.get(v).copyFrom(map.get(v));
             }
         }
 
         void restore() {
             // Restore current state from savedMap
             for (View v: savedMap.keySet()) {
-                savedMap.get(v).copy(map.get(v));
+                map.get(v).copyFrom(savedMap.get(v));
             }
         }
 
@@ -2579,35 +2484,21 @@
         }
 
         int area() {
-            return dragViewSpanX * dragViewSpanY;
-        }
-    }
-
-    private class CellAndSpan {
-        int x, y;
-        int spanX, spanY;
-
-        public CellAndSpan() {
+            return spanX * spanY;
         }
 
-        public void copy(CellAndSpan copy) {
-            copy.x = x;
-            copy.y = y;
-            copy.spanX = spanX;
-            copy.spanY = spanY;
+        void getBoundingRectForViews(ArrayList<View> views, Rect outRect) {
+            boolean first = true;
+            for (View v: views) {
+                CellAndSpan c = map.get(v);
+                if (first) {
+                    outRect.set(c.cellX, c.cellY, c.cellX + c.spanX, c.cellY + c.spanY);
+                    first = false;
+                } else {
+                    outRect.union(c.cellX, c.cellY, c.cellX + c.spanX, c.cellY + c.spanY);
+                }
+            }
         }
-
-        public CellAndSpan(int x, int y, int spanX, int spanY) {
-            this.x = x;
-            this.y = y;
-            this.spanX = spanX;
-            this.spanY = spanY;
-        }
-
-        public String toString() {
-            return "(" + x + ", " + y + ": " + spanX + ", " + spanY + ")";
-        }
-
     }
 
     /**
@@ -2623,7 +2514,7 @@
      * @return The X, Y cell of a vacant area that can contain this object,
      *         nearest the requested location.
      */
-    int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, int[] result) {
+    public int[] findNearestArea(int pixelX, int pixelY, int spanX, int spanY, int[] result) {
         return findNearestArea(pixelX, pixelY, spanX, spanY, spanX, spanY, false, result, null);
     }
 
@@ -2645,33 +2536,10 @@
      * @return True if a vacant cell of the specified dimension was found, false otherwise.
      */
     public boolean findCellForSpan(int[] cellXY, int spanX, int spanY) {
-        boolean foundCell = false;
-        final int endX = mCountX - (spanX - 1);
-        final int endY = mCountY - (spanY - 1);
-
-        for (int y = 0; y < endY && !foundCell; y++) {
-            inner:
-            for (int x = 0; x < endX; x++) {
-                for (int i = 0; i < spanX; i++) {
-                    for (int j = 0; j < spanY; j++) {
-                        if (mOccupied[x + i][y + j]) {
-                            // small optimization: we can skip to after the column we just found
-                            // an occupied cell
-                            x += i;
-                            continue inner;
-                        }
-                    }
-                }
-                if (cellXY != null) {
-                    cellXY[0] = x;
-                    cellXY[1] = y;
-                }
-                foundCell = true;
-                break;
-            }
+        if (cellXY == null) {
+            cellXY = new int[2];
         }
-
-        return foundCell;
+        return mOccupied.findVacantCell(cellXY, spanX, spanY);
     }
 
     /**
@@ -2745,34 +2613,16 @@
         resultRect.set(x, y, x + width, y + height);
     }
 
-    private void clearOccupiedCells() {
-        for (int x = 0; x < mCountX; x++) {
-            for (int y = 0; y < mCountY; y++) {
-                mOccupied[x][y] = false;
-            }
-        }
-    }
-
     public void markCellsAsOccupiedForView(View view) {
         if (view == null || view.getParent() != mShortcutsAndWidgets) return;
         LayoutParams lp = (LayoutParams) view.getLayoutParams();
-        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, mOccupied, true);
+        mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
     }
 
     public void markCellsAsUnoccupiedForView(View view) {
         if (view == null || view.getParent() != mShortcutsAndWidgets) return;
         LayoutParams lp = (LayoutParams) view.getLayoutParams();
-        markCellsForView(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, mOccupied, false);
-    }
-
-    private void markCellsForView(int cellX, int cellY, int spanX, int spanY, boolean[][] occupied,
-            boolean value) {
-        if (cellX < 0 || cellY < 0) return;
-        for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
-            for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
-                occupied[x][y] = value;
-            }
-        }
+        mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
     }
 
     public int getDesiredWidth() {
@@ -2787,7 +2637,7 @@
 
     public boolean isOccupied(int x, int y) {
         if (x < mCountX && y < mCountY) {
-            return mOccupied[x][y];
+            return mOccupied.cells[x][y];
         } else {
             throw new RuntimeException("Position exceeds the bound of this CellLayout");
         }
@@ -2861,16 +2711,16 @@
 
         /**
          * Indicates whether this item can be reordered. Always true except in the case of the
-         * the AllApps button.
+         * the AllApps button and QSB place holder.
          */
         public boolean canReorder = true;
 
         // X coordinate of the view in the layout.
         @ViewDebug.ExportedProperty
-        int x;
+        public int x;
         // Y coordinate of the view in the layout.
         @ViewDebug.ExportedProperty
-        int y;
+        public int y;
 
         boolean dropped;
 
@@ -2966,21 +2816,17 @@
     // 2. When long clicking on an empty cell in a CellLayout, we save information about the
     //    cellX and cellY coordinates and which page was clicked. We then set this as a tag on
     //    the CellLayout that was long clicked
-    public static final class CellInfo {
-        View cell;
-        int cellX = -1;
-        int cellY = -1;
-        int spanX;
-        int spanY;
+    public static final class CellInfo extends CellAndSpan {
+        public View cell;
         long screenId;
         long container;
 
         public CellInfo(View v, ItemInfo info) {
-            cell = v;
             cellX = info.cellX;
             cellY = info.cellY;
             spanX = info.spanX;
             spanY = info.spanY;
+            cell = v;
             screenId = info.screenId;
             container = info.container;
         }
@@ -2992,24 +2838,27 @@
         }
     }
 
-    public boolean findVacantCell(int spanX, int spanY, int[] outXY) {
-        return Utilities.findVacantCell(outXY, spanX, spanY, mCountX, mCountY, mOccupied);
-    }
-
-    public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
-        int x2 = x + spanX - 1;
-        int y2 = y + spanY - 1;
-        if (x < 0 || y < 0 || x2 >= mCountX || y2 >= mCountY) {
-            return false;
-        }
-        for (int i = x; i <= x2; i++) {
-            for (int j = y; j <= y2; j++) {
-                if (mOccupied[i][j]) {
-                    return false;
+    /**
+     * Returns whether an item can be placed in this CellLayout (after rearranging and/or resizing
+     * if necessary).
+     */
+    public boolean hasReorderSolution(ItemInfo itemInfo) {
+        int[] cellPoint = new int[2];
+        // Check for a solution starting at every cell.
+        for (int cellX = 0; cellX < getCountX(); cellX++) {
+            for (int cellY = 0; cellY < getCountY(); cellY++) {
+                cellToPoint(cellX, cellY, cellPoint);
+                if (findReorderSolution(cellPoint[0], cellPoint[1], itemInfo.minSpanX,
+                        itemInfo.minSpanY, itemInfo.spanX, itemInfo.spanY, mDirectionVector, null,
+                        true, new ItemConfiguration()).isSolution) {
+                    return true;
                 }
             }
         }
+        return false;
+    }
 
-        return true;
+    public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
+        return mOccupied.isRegionVacant(x, y, spanX, spanY);
     }
 }
diff --git a/src/com/android/launcher3/CheckLongPressHelper.java b/src/com/android/launcher3/CheckLongPressHelper.java
index 483c622..dde733c 100644
--- a/src/com/android/launcher3/CheckLongPressHelper.java
+++ b/src/com/android/launcher3/CheckLongPressHelper.java
@@ -22,10 +22,12 @@
 
 public class CheckLongPressHelper {
 
+    public static final int DEFAULT_LONG_PRESS_TIMEOUT = 300;
+
     @Thunk View mView;
     @Thunk View.OnLongClickListener mListener;
     @Thunk boolean mHasPerformedLongPress;
-    private int mLongPressTimeout = 300;
+    private int mLongPressTimeout = DEFAULT_LONG_PRESS_TIMEOUT;
     private CheckForLongPress mPendingCheckForLongPress;
 
     class CheckForLongPress implements Runnable {
diff --git a/src/com/android/launcher3/ClickShadowView.java b/src/com/android/launcher3/ClickShadowView.java
index e31d7f7..aad1112 100644
--- a/src/com/android/launcher3/ClickShadowView.java
+++ b/src/com/android/launcher3/ClickShadowView.java
@@ -21,7 +21,9 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
+import android.graphics.Rect;
 import android.view.View;
+import android.view.ViewDebug;
 import android.view.ViewGroup;
 
 public class ClickShadowView extends View {
@@ -32,7 +34,9 @@
 
     private final Paint mPaint;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     private final float mShadowOffset;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private final float mShadowPadding;
 
     private Bitmap mBitmap;
@@ -88,13 +92,27 @@
      * Aligns the shadow with {@param view}
      * @param viewParent immediate parent of {@param view}. It must be a sibling of this view.
      */
-    public void alignWithIconView(BubbleTextView view, ViewGroup viewParent) {
+    public void alignWithIconView(BubbleTextView view, ViewGroup viewParent, View clipAgainstView) {
         float leftShift = view.getLeft() + viewParent.getLeft() - getLeft();
         float topShift = view.getTop() + viewParent.getTop() - getTop();
         int iconWidth = view.getRight() - view.getLeft();
+        int iconHeight = view.getBottom() - view.getTop();
         int iconHSpace = iconWidth - view.getCompoundPaddingRight() - view.getCompoundPaddingLeft();
         float drawableWidth = view.getIcon().getBounds().width();
 
+        if (clipAgainstView != null) {
+            // Set the bounds to clip against
+            int[] coords = new int[] {0, 0};
+            Utilities.getDescendantCoordRelativeToAncestor(clipAgainstView, (View) getParent(),
+                    coords, false);
+            int clipLeft = (int) Math.max(0, coords[0] - leftShift - mShadowPadding);
+            int clipTop = (int) Math.max(0, coords[1] - topShift - mShadowPadding) ;
+            setClipBounds(new Rect(clipLeft, clipTop, clipLeft + iconWidth, clipTop + iconHeight));
+        } else {
+            // Reset the clip bounds
+            setClipBounds(null);
+        }
+
         setTranslationX(leftShift
                 + viewParent.getTranslationX()
                 + view.getCompoundPaddingLeft() * view.getScaleX()
diff --git a/src/com/android/launcher3/CommonAppTypeParser.java b/src/com/android/launcher3/CommonAppTypeParser.java
index 001e044..c2bd883 100644
--- a/src/com/android/launcher3/CommonAppTypeParser.java
+++ b/src/com/android/launcher3/CommonAppTypeParser.java
@@ -25,7 +25,6 @@
 
 import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
 import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.backup.nano.BackupProtos.Favorite;
 import com.android.launcher3.util.Thunk;
 
 import org.xmlpull.v1.XmlPullParserException;
@@ -43,6 +42,12 @@
 
     private static final int RESTORE_FLAG_BIT_SHIFT = 4;
 
+    public static final int TARGET_PHONE = 1;
+    public static final int TARGET_MESSENGER = 2;
+    public static final int TARGET_EMAIL = 3;
+    public static final int TARGET_BROWSER = 4;
+    public static final int TARGET_GALLERY = 5;
+    public static final int TARGET_CAMERA = 6;
 
     private final long mItemId;
     @Thunk final int mResId;
@@ -68,7 +73,6 @@
         parsedValues = values;
 
         // Remove unwanted values
-        values.put(Favorites.ICON_TYPE, (Integer) null);
         values.put(Favorites.ICON_PACKAGE, (String) null);
         values.put(Favorites.ICON_RESOURCE, (String) null);
         values.put(Favorites.ICON, (byte[]) null);
@@ -119,22 +123,22 @@
 
     public static int getResourceForItemType(int type) {
         switch (type) {
-            case Favorite.TARGET_PHONE:
+            case TARGET_PHONE:
                 return R.xml.app_target_phone;
 
-            case Favorite.TARGET_MESSENGER:
+            case TARGET_MESSENGER:
                 return R.xml.app_target_messenger;
 
-            case Favorite.TARGET_EMAIL:
+            case TARGET_EMAIL:
                 return R.xml.app_target_email;
 
-            case Favorite.TARGET_BROWSER:
+            case TARGET_BROWSER:
                 return R.xml.app_target_browser;
 
-            case Favorite.TARGET_GALLERY:
+            case TARGET_GALLERY:
                 return R.xml.app_target_gallery;
 
-            case Favorite.TARGET_CAMERA:
+            case TARGET_CAMERA:
                 return R.xml.app_target_camera;
 
             default:
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index 2bba380..ef28d1e 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -1,6 +1,8 @@
 package com.android.launcher3;
 
 import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -9,6 +11,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -33,7 +36,7 @@
     private static final String TAG_FAVORITES = "favorites";
     protected static final String TAG_FAVORITE = "favorite";
     private static final String TAG_APPWIDGET = "appwidget";
-    private static final String TAG_SHORTCUT = "shortcut";
+    protected static final String TAG_SHORTCUT = "shortcut";
     private static final String TAG_FOLDER = "folder";
     private static final String TAG_PARTNER_FOLDER = "partner-folder";
 
@@ -42,6 +45,10 @@
     private static final String ATTR_SCREEN = "screen";
     private static final String ATTR_FOLDER_ITEMS = "folderItems";
 
+    // TODO: Remove support for this broadcast, instead use widget options to send bind time options
+    private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
+            "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
+
     public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost,
             LayoutParserCallback callback, Resources sourceRes, int layoutId) {
         super(context, appWidgetHost, callback, sourceRes, layoutId, TAG_FAVORITES);
@@ -89,7 +96,7 @@
     /**
      * AppShortcutParser which also supports adding URI based intents
      */
-    @Thunk class AppShortcutWithUriParser extends AppShortcutParser {
+    public class AppShortcutWithUriParser extends AppShortcutParser {
 
         @Override
         protected long invalidPackageOrClass(XmlResourceParser parser) {
@@ -179,7 +186,7 @@
     /**
      * Shortcut parser which allows any uri and not just web urls.
      */
-    private class UriShortcutParser extends ShortcutParser {
+    public class UriShortcutParser extends ShortcutParser {
 
         public UriShortcutParser(Resources iconRes) {
             super(iconRes);
@@ -201,7 +208,7 @@
     /**
      * Contains a list of <favorite> nodes, and accepts the first successfully parsed node.
      */
-    protected class ResolveParser implements TagParser {
+    public class ResolveParser implements TagParser {
 
         private final AppShortcutWithUriParser mChildParser = new AppShortcutWithUriParser();
 
@@ -270,4 +277,61 @@
             return super.parseAndAdd(parser);
         }
     }
+
+
+    /**
+     * AppWidget parser which enforces that the app is already installed when the layout is parsed.
+     */
+    protected class AppWidgetParser extends PendingWidgetParser {
+
+        @Override
+        protected long verifyAndInsert(ComponentName cn, Bundle extras) {
+            try {
+                mPackageManager.getReceiverInfo(cn, 0);
+            } catch (Exception e) {
+                String[] packages = mPackageManager.currentToCanonicalPackageNames(
+                        new String[] { cn.getPackageName() });
+                cn = new ComponentName(packages[0], cn.getClassName());
+                try {
+                    mPackageManager.getReceiverInfo(cn, 0);
+                } catch (Exception e1) {
+                    Log.d(TAG, "Can't find widget provider: " + cn.getClassName());
+                    return -1;
+                }
+            }
+
+            final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
+            long insertedId = -1;
+            try {
+                int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+
+                if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn)) {
+                    Log.e(TAG, "Unable to bind app widget id " + cn);
+                    mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+                    return -1;
+                }
+
+                mValues.put(Favorites.APPWIDGET_ID, appWidgetId);
+                mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
+                mValues.put(Favorites._ID, mCallback.generateNewItemId());
+                insertedId = mCallback.insertAndCheck(mDb, mValues);
+                if (insertedId < 0) {
+                    mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+                    return insertedId;
+                }
+
+                // Send a broadcast to configure the widget
+                if (!extras.isEmpty()) {
+                    Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
+                    intent.setComponent(cn);
+                    intent.putExtras(extras);
+                    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+                    mContext.sendBroadcast(intent);
+                }
+            } catch (RuntimeException ex) {
+                Log.e(TAG, "Problem allocating appWidgetId", ex);
+            }
+            return insertedId;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index edaf525..705f841 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -19,10 +19,14 @@
 import android.animation.TimeInterpolator;
 import android.content.Context;
 import android.graphics.PointF;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.animation.AnimationUtils;
 
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.folder.Folder;
 import com.android.launcher3.util.FlingAnimation;
 import com.android.launcher3.util.Thunk;
 
@@ -45,20 +49,37 @@
         setDrawable(R.drawable.ic_remove_launcher);
     }
 
-    public static boolean supportsDrop(Object info) {
+    @Override
+    public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+        super.onDragStart(dragObject, options);
+        setTextBasedOnDragSource(dragObject.dragSource);
+    }
+
+    /** @return true for items that should have a "Remove" action in accessibility. */
+    public static boolean supportsAccessibleDrop(ItemInfo info) {
         return (info instanceof ShortcutInfo)
                 || (info instanceof LauncherAppWidgetInfo)
                 || (info instanceof FolderInfo);
     }
 
     @Override
-    protected boolean supportsDrop(DragSource source, Object info) {
-        return source.supportsDeleteDropTarget() && supportsDrop(info);
+    protected boolean supportsDrop(DragSource source, ItemInfo info) {
+        return true;
+    }
+
+    /**
+     * Set the drop target's text to either "Remove" or "Cancel" depending on the drag source.
+     */
+    public void setTextBasedOnDragSource(DragSource dragSource) {
+        if (!TextUtils.isEmpty(getText())) {
+            setText(dragSource.supportsDeleteDropTarget() ? R.string.remove_drop_target_label
+                    : android.R.string.cancel);
+        }
     }
 
     @Override
     @Thunk void completeDrop(DragObject d) {
-        ItemInfo item = (ItemInfo) d.dragInfo;
+        ItemInfo item = d.dragInfo;
         if ((d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder)) {
             removeWorkspaceOrFolderItem(mLauncher, item, null);
         }
@@ -80,7 +101,6 @@
     public void onFlingToDelete(final DragObject d, PointF vel) {
         // Don't highlight the icon as it's animating
         d.dragView.setColor(0);
-        d.dragView.updateInitialScaleToCurrentScale();
 
         final DragLayer dragLayer = mLauncher.getDragLayer();
         FlingAnimation fling = new FlingAnimation(d, vel,
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 380c6b1..f9f8e80 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -20,8 +20,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.Paint;
-import android.graphics.Paint.FontMetrics;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
@@ -29,12 +27,18 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
-import android.view.ViewGroup.MarginLayoutParams;
 import android.widget.FrameLayout;
-import android.widget.LinearLayout;
+
+import com.android.launcher3.config.FeatureFlags;
+
+import java.util.ArrayList;
 
 public class DeviceProfile {
 
+    public interface LauncherLayoutChangeListener {
+        void onLauncherLayoutChanged();
+    }
+
     public final InvariantDeviceProfile inv;
 
     // Device properties
@@ -67,9 +71,17 @@
     private int desiredWorkspaceLeftRightMarginPx;
     public final int edgeMarginPx;
     public final Rect defaultWidgetPadding;
-    private final int pageIndicatorHeightPx;
     private final int defaultPageSpacingPx;
+    private final int topWorkspacePadding;
     private float dragViewScale;
+    public float workspaceSpringLoadShrinkFactor;
+    public final int workspaceSpringLoadedBottomSpace;
+
+    // Page indicator
+    private final int pageIndicatorHeightPx;
+    private final int pageIndicatorLandGutterLeftNavBarPx;
+    private final int pageIndicatorLandGutterRightNavBarPx;
+    private final int pageIndicatorLandWorkspaceOffsetPx;
 
     // Workspace icons
     public int iconSizePx;
@@ -83,32 +95,39 @@
     // Folder
     public int folderBackgroundOffset;
     public int folderIconSizePx;
+    public int folderIconPreviewPadding;
     public int folderCellWidthPx;
     public int folderCellHeightPx;
+    public int folderChildDrawablePaddingPx;
 
     // Hotseat
     public int hotseatCellWidthPx;
     public int hotseatCellHeightPx;
     public int hotseatIconSizePx;
-    private int normalHotseatBarHeightPx, shortHotseatBarHeightPx;
-    private int hotseatBarHeightPx; // One of the above.
+    private int hotseatBarHeightPx;
+    private int hotseatBarTopPaddingPx;
+    private int hotseatLandGutterPx;
 
     // All apps
     public int allAppsNumCols;
     public int allAppsNumPredictiveCols;
     public int allAppsButtonVisualSize;
-    public final int allAppsIconSizePx;
-    public final float allAppsIconTextSizeSp;
+    public int allAppsIconSizePx;
+    public int allAppsIconDrawablePaddingPx;
+    public float allAppsIconTextSizePx;
 
-    // QSB
-    private int searchBarWidgetInternalPaddingTop, searchBarWidgetInternalPaddingBottom;
-    private int searchBarTopPaddingPx;
-    private int tallSearchBarNegativeTopPaddingPx, normalSearchBarTopExtraPaddingPx;
-    private int searchBarTopExtraPaddingPx; // One of the above.
-    private int normalSearchBarBottomPaddingPx, tallSearchBarBottomPaddingPx;
-    private int searchBarBottomPaddingPx; // One of the above.
-    private int normalSearchBarSpaceHeightPx, tallSearchBarSpaceHeightPx;
-    private int searchBarSpaceHeightPx; // One of the above.
+    // Containers
+    private final int containerLeftPaddingPx;
+    private final int containerRightPaddingPx;
+
+    // Drop Target
+    public int dropTargetBarSizePx;
+
+    // Insets
+    private Rect mInsets = new Rect();
+
+    // Listeners
+    private ArrayList<LauncherLayoutChangeListener> mListeners = new ArrayList<>();
 
     public DeviceProfile(Context context, InvariantDeviceProfile inv,
             Point minSize, Point maxSize,
@@ -133,11 +152,19 @@
                 this.getClass().getName());
         defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
         edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
-        desiredWorkspaceLeftRightMarginPx = 2 * edgeMarginPx;
+        desiredWorkspaceLeftRightMarginPx = edgeMarginPx;
         pageIndicatorHeightPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_height);
+        pageIndicatorLandGutterLeftNavBarPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_page_indicator_gutter_width_left_nav_bar);
+        pageIndicatorLandWorkspaceOffsetPx =
+                res.getDimensionPixelSize(R.dimen.all_apps_caret_workspace_offset);
+        pageIndicatorLandGutterRightNavBarPx = res.getDimensionPixelSize(
+                R.dimen.dynamic_grid_page_indicator_gutter_width_right_nav_bar);
         defaultPageSpacingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
+        topWorkspacePadding =
+                res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_top_padding);
         overviewModeMinIconZoneHeightPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_min_icon_zone_height);
         overviewModeMaxIconZoneHeightPx =
@@ -150,12 +177,17 @@
                 res.getInteger(R.integer.config_dynamic_grid_overview_icon_zone_percentage) / 100f;
         iconDrawablePaddingOriginalPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
-
-        // AllApps uses the original non-scaled icon text size
-        allAppsIconTextSizeSp = inv.iconTextSize;
-
-        // AllApps uses the original non-scaled icon size
-        allAppsIconSizePx = Utilities.pxFromDp(inv.iconSize, dm);
+        dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size);
+        workspaceSpringLoadedBottomSpace =
+                res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
+        hotseatBarHeightPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_height);
+        hotseatBarTopPaddingPx =
+                res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
+        hotseatLandGutterPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_gutter_width);
+        containerLeftPaddingPx =
+                res.getDimensionPixelSize(R.dimen.dynamic_grid_container_land_left_padding);
+        containerRightPaddingPx =
+                res.getDimensionPixelSize(R.dimen.dynamic_grid_container_land_right_padding);
 
         // Determine sizes.
         widthPx = width;
@@ -173,6 +205,18 @@
         computeAllAppsButtonSize(context);
     }
 
+    public void addLauncherLayoutChangedListener(LauncherLayoutChangeListener listener) {
+        if (!mListeners.contains(listener)) {
+            mListeners.add(listener);
+        }
+    }
+
+    public void removeLauncherLayoutChangedListener(LauncherLayoutChangeListener listener) {
+        if (mListeners.contains(listener)) {
+            mListeners.remove(listener);
+        }
+    }
+
     /**
      * Determine the exact visual footprint of the all apps button, taking into account scaling
      * and internal padding of the drawable.
@@ -192,9 +236,7 @@
         updateIconSize(1f, drawablePadding, res, dm);
         float usedHeight = (cellHeightPx * inv.numRows);
 
-        // We only care about the top and bottom workspace padding, which is not affected by RTL.
-        Rect workspacePadding = getWorkspacePadding(false /* isLayoutRtl */);
-        int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom);
+        int maxHeight = (availableHeightPx - getTotalWorkspacePadding().y);
         if (usedHeight > maxHeight) {
             scale = maxHeight / usedHeight;
             drawablePadding = 0;
@@ -208,139 +250,120 @@
         iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
         iconDrawablePaddingPx = drawablePadding;
         hotseatIconSizePx = (int) (Utilities.pxFromDp(inv.hotseatIconSize, dm) * scale);
+        allAppsIconSizePx = iconSizePx;
+        allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
+        allAppsIconTextSizePx = iconTextSizePx;
 
-        // Search Bar
-        normalSearchBarSpaceHeightPx = res.getDimensionPixelSize(
-                R.dimen.dynamic_grid_search_bar_height);
-        tallSearchBarSpaceHeightPx = res.getDimensionPixelSize(
-                R.dimen.dynamic_grid_search_bar_height_tall);
-        searchBarWidgetInternalPaddingTop = res.getDimensionPixelSize(
-                R.dimen.qsb_internal_padding_top);
-        searchBarWidgetInternalPaddingBottom = res.getDimensionPixelSize(
-                R.dimen.qsb_internal_padding_bottom);
-        normalSearchBarTopExtraPaddingPx = res.getDimensionPixelSize(
-                R.dimen.dynamic_grid_search_bar_extra_top_padding);
-        tallSearchBarNegativeTopPaddingPx = res.getDimensionPixelSize(
-                R.dimen.dynamic_grid_search_bar_negative_top_padding_short);
-        if (isTablet && !isVerticalBarLayout()) {
-            searchBarTopPaddingPx = searchBarWidgetInternalPaddingTop;
-            normalSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom +
-                    res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_bottom_padding_tablet);
-            tallSearchBarBottomPaddingPx = normalSearchBarBottomPaddingPx;
-        } else {
-            searchBarTopPaddingPx = searchBarWidgetInternalPaddingTop;
-            normalSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom +
-                    res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_bottom_padding);
-            tallSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom
-                    + res.getDimensionPixelSize(
-                    R.dimen.dynamic_grid_search_bar_bottom_negative_padding_short);
-        }
-
-        // Calculate the actual text height
-        Paint textPaint = new Paint();
-        textPaint.setTextSize(iconTextSizePx);
-        FontMetrics fm = textPaint.getFontMetrics();
         cellWidthPx = iconSizePx;
-        cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top);
-        final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale);
+        cellHeightPx = iconSizePx + iconDrawablePaddingPx
+                + Utilities.calculateTextHeight(iconTextSizePx);
+        final float scaleDps = !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? 0f
+                : res.getDimensionPixelSize(R.dimen.dragViewScale);
         dragViewScale = (iconSizePx + scaleDps) / iconSizePx;
 
         // Hotseat
-        normalHotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx;
-        shortHotseatBarHeightPx = iconSizePx + 2 * edgeMarginPx;
         hotseatCellWidthPx = iconSizePx;
         hotseatCellHeightPx = iconSizePx;
 
-        // Folder
-        int folderCellPadding = isTablet || isLandscape ? 6 * edgeMarginPx : 3 * edgeMarginPx;
+        if (!isVerticalBarLayout()) {
+            int expectedWorkspaceHeight = availableHeightPx - hotseatBarHeightPx
+                    - pageIndicatorHeightPx - topWorkspacePadding;
+            float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace;
+            workspaceSpringLoadShrinkFactor = Math.min(
+                    res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f,
+                    1 - (minRequiredHeight / expectedWorkspaceHeight));
+        } else {
+            workspaceSpringLoadShrinkFactor =
+                    res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
+        }
+
+        // Folder cell
+        int cellPaddingX = res.getDimensionPixelSize(R.dimen.folder_cell_x_padding);
+        int cellPaddingY = res.getDimensionPixelSize(R.dimen.folder_cell_y_padding);
+        final int folderChildTextSize =
+                Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_child_text_size));
+
+        final int folderBottomPanelSize =
+                res.getDimensionPixelSize(R.dimen.folder_label_padding_top)
+                 + res.getDimensionPixelSize(R.dimen.folder_label_padding_bottom)
+                + Utilities.calculateTextHeight(res.getDimension(R.dimen.folder_label_text_size));
+
         // Don't let the folder get too close to the edges of the screen.
-        folderCellWidthPx = Math.min(cellWidthPx + folderCellPadding,
+        folderCellWidthPx = Math.min(iconSizePx + 2 * cellPaddingX,
                 (availableWidthPx - 4 * edgeMarginPx) / inv.numFolderColumns);
-        folderCellHeightPx = cellHeightPx + edgeMarginPx;
+        folderCellHeightPx = Math.min(iconSizePx + 3 * cellPaddingY + folderChildTextSize,
+                (availableHeightPx - 4 * edgeMarginPx - folderBottomPanelSize) / inv.numFolderRows);
+        folderChildDrawablePaddingPx = Math.max(0,
+                (folderCellHeightPx - iconSizePx - folderChildTextSize) / 3);
+
+        // Folder icon
         folderBackgroundOffset = -edgeMarginPx;
         folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
+        folderIconPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
     }
 
-    /**
-     * @param recyclerViewWidth the available width of the AllAppsRecyclerView
-     */
-    public void updateAppsViewNumCols(Resources res, int recyclerViewWidth) {
-        int appsViewLeftMarginPx =
-                res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
-        int allAppsCellWidthGap =
-                res.getDimensionPixelSize(R.dimen.all_apps_icon_width_gap);
-        int availableAppsWidthPx = (recyclerViewWidth > 0) ? recyclerViewWidth : availableWidthPx;
-        int numAppsCols = (availableAppsWidthPx + allAppsCellWidthGap - appsViewLeftMarginPx) /
-                (allAppsIconSizePx + allAppsCellWidthGap);
-        int numPredictiveAppCols = Math.max(inv.minAllAppsPredictionColumns, numAppsCols);
-        allAppsNumCols = numAppsCols;
-        allAppsNumPredictiveCols = numPredictiveAppCols;
+    public void updateInsets(Rect insets) {
+        mInsets.set(insets);
     }
 
-    /** Returns the amount of extra space to allocate to the search bar for vertical padding. */
-    private int getSearchBarTotalVerticalPadding() {
-        return searchBarTopPaddingPx + searchBarTopExtraPaddingPx + searchBarBottomPaddingPx;
+    public void updateAppsViewNumCols() {
+        allAppsNumCols = allAppsNumPredictiveCols = inv.numColumns;
     }
 
     /** Returns the width and height of the search bar, ignoring any padding. */
-    public Point getSearchBarDimensForWidgetOpts(Resources res) {
-        Rect searchBarBounds = getSearchBarBounds(Utilities.isRtl(res));
+    public Point getSearchBarDimensForWidgetOpts() {
         if (isVerticalBarLayout()) {
-            return new Point(searchBarBounds.width(), searchBarBounds.height());
-        }
-        int widgetInternalPadding = searchBarWidgetInternalPaddingTop +
-                searchBarWidgetInternalPaddingBottom;
-        return new Point(searchBarBounds.width(), searchBarSpaceHeightPx + widgetInternalPadding);
-    }
-
-    /** Returns the search bar bounds in the current orientation */
-    public Rect getSearchBarBounds(boolean isLayoutRtl) {
-        Rect bounds = new Rect();
-        if (isVerticalBarLayout()) {
-            if (isLayoutRtl) {
-                bounds.set(availableWidthPx - normalSearchBarSpaceHeightPx, edgeMarginPx,
-                        availableWidthPx, availableHeightPx - edgeMarginPx);
-            } else {
-                bounds.set(0, edgeMarginPx, normalSearchBarSpaceHeightPx,
-                        availableHeightPx - edgeMarginPx);
-            }
+            return new Point(dropTargetBarSizePx, availableHeightPx - 2 * edgeMarginPx);
         } else {
-            int boundsBottom = searchBarSpaceHeightPx + getSearchBarTotalVerticalPadding();
+            int gap;
             if (isTablet) {
                 // Pad the left and right of the workspace to ensure consistent spacing
                 // between all icons
                 int width = getCurrentWidth();
                 // XXX: If the icon size changes across orientations, we will have to take
                 //      that into account here too.
-                int gap = (int) ((width - 2 * edgeMarginPx -
-                        (inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1)));
-                bounds.set(edgeMarginPx + gap, 0,
-                        availableWidthPx - (edgeMarginPx + gap), boundsBottom);
+                gap = ((width - 2 * edgeMarginPx
+                        - (inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1)))
+                        + edgeMarginPx;
             } else {
-                bounds.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
-                        0,
-                        availableWidthPx - (desiredWorkspaceLeftRightMarginPx -
-                        defaultWidgetPadding.right), boundsBottom);
+                gap = desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right;
             }
+            return new Point(availableWidthPx - 2 * gap, dropTargetBarSizePx);
         }
-        return bounds;
     }
 
-    /** Returns the workspace padding in the specified orientation */
-    Rect getWorkspacePadding(boolean isLayoutRtl) {
-        Rect searchBarBounds = getSearchBarBounds(isLayoutRtl);
-        Rect padding = new Rect();
+    public Point getCellSize() {
+        Point result = new Point();
+        // Since we are only concerned with the overall padding, layout direction does
+        // not matter.
+        Point padding = getTotalWorkspacePadding();
+        result.x = calculateCellWidth(availableWidthPx - padding.x, inv.numColumns);
+        result.y = calculateCellHeight(availableHeightPx - padding.y, inv.numRows);
+        return result;
+    }
+
+    public Point getTotalWorkspacePadding() {
+        Rect padding = getWorkspacePadding(null);
+        return new Point(padding.left + padding.right, padding.top + padding.bottom);
+    }
+
+    /**
+     * Returns the workspace padding in the specified orientation.
+     * Note that it assumes that while in verticalBarLayout, the nav bar is on the right, as such
+     * this value is not reliable.
+     * Use {@link #getTotalWorkspacePadding()} instead.
+     */
+    public Rect getWorkspacePadding(Rect recycle) {
+        Rect padding = recycle == null ? new Rect() : recycle;
         if (isVerticalBarLayout()) {
-            // Pad the left and right of the workspace with search/hotseat bar sizes
-            if (isLayoutRtl) {
-                padding.set(normalHotseatBarHeightPx, edgeMarginPx,
-                        searchBarBounds.width(), edgeMarginPx);
+            if (mInsets.left > 0) {
+                padding.set(mInsets.left + pageIndicatorLandGutterLeftNavBarPx, 0,
+                        hotseatBarHeightPx + hotseatLandGutterPx - mInsets.left, 2 * edgeMarginPx);
             } else {
-                padding.set(searchBarBounds.width(), edgeMarginPx,
-                        normalHotseatBarHeightPx, edgeMarginPx);
+                padding.set(pageIndicatorLandGutterRightNavBarPx, 0,
+                        hotseatBarHeightPx + hotseatLandGutterPx, 2 * edgeMarginPx);
             }
         } else {
-            int paddingTop = searchBarBounds.bottom;
             int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
             if (isTablet) {
                 // Pad the left and right of the workspace to ensure consistent spacing
@@ -353,22 +376,42 @@
                         ((inv.numColumns - 1) * gapScale * cellWidthPx)));
                 availablePaddingX = (int) Math.min(availablePaddingX,
                             width * MAX_HORIZONTAL_PADDING_PERCENT);
-                int availablePaddingY = Math.max(0, height - paddingTop - paddingBottom
+                int availablePaddingY = Math.max(0, height - topWorkspacePadding - paddingBottom
                         - (int) (2 * inv.numRows * cellHeightPx));
-                padding.set(availablePaddingX / 2, paddingTop + availablePaddingY / 2,
+                padding.set(availablePaddingX / 2, topWorkspacePadding + availablePaddingY / 2,
                         availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
             } else {
                 // Pad the top and bottom of the workspace with search/hotseat bar sizes
-                padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
-                        paddingTop,
-                        desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right,
+                padding.set(desiredWorkspaceLeftRightMarginPx,
+                        topWorkspacePadding,
+                        desiredWorkspaceLeftRightMarginPx,
                         paddingBottom);
             }
         }
         return padding;
     }
 
-    private int getWorkspacePageSpacing(boolean isLayoutRtl) {
+    /**
+     * @return the bounds for which the open folders should be contained within
+     */
+    public Rect getAbsoluteOpenFolderBounds() {
+        if (isVerticalBarLayout()) {
+            // Folders should only appear right of the drop target bar and left of the hotseat
+            return new Rect(mInsets.left + dropTargetBarSizePx + edgeMarginPx,
+                    mInsets.top,
+                    mInsets.left + availableWidthPx - hotseatBarHeightPx - edgeMarginPx,
+                    mInsets.top + availableHeightPx);
+        } else {
+            // Folders should only appear below the drop target bar and above the hotseat
+            return new Rect(mInsets.left,
+                    mInsets.top + dropTargetBarSizePx + edgeMarginPx,
+                    mInsets.left + availableWidthPx,
+                    mInsets.top + availableHeightPx - hotseatBarHeightPx - pageIndicatorHeightPx -
+                            edgeMarginPx);
+        }
+    }
+
+    private int getWorkspacePageSpacing() {
         if (isVerticalBarLayout() || isLargeTablet) {
             // In landscape mode the page spacing is set to the default.
             return defaultPageSpacingPx;
@@ -376,7 +419,7 @@
             // In portrait, we want the pages spaced such that there is no
             // overhang of the previous / next page into the current page viewport.
             // We assume symmetrical padding in portrait mode.
-            return Math.max(defaultPageSpacingPx, 2 * getWorkspacePadding(isLayoutRtl).left);
+            return Math.max(defaultPageSpacingPx, getWorkspacePadding(null).left + 1);
         }
     }
 
@@ -387,17 +430,6 @@
         return zoneHeight;
     }
 
-    // The rect returned will be extended to below the system ui that covers the workspace
-    Rect getHotseatRect() {
-        if (isVerticalBarLayout()) {
-            return new Rect(availableWidthPx - normalHotseatBarHeightPx, 0,
-                    Integer.MAX_VALUE, availableHeightPx);
-        } else {
-            return new Rect(0, availableHeightPx - hotseatBarHeightPx,
-                    availableWidthPx, Integer.MAX_VALUE);
-        }
-    }
-
     public static int calculateCellWidth(int width, int countX) {
         return width / countX;
     }
@@ -410,7 +442,7 @@
      * When {@code false}, either device is in portrait mode or the device is in landscape mode and
      * the hotseat is on the bottom row.
      */
-    boolean isVerticalBarLayout() {
+    public boolean isVerticalBarLayout() {
         return isLandscape && transposeLayoutWithOrientation;
     }
 
@@ -428,62 +460,34 @@
         return visibleChildren;
     }
 
-    // TODO(twickham): b/25154513
-    public void setSearchBarHeight(int searchBarHeight) {
-        if (searchBarHeight == LauncherCallbacks.SEARCH_BAR_HEIGHT_TALL) {
-            hotseatBarHeightPx = shortHotseatBarHeightPx;
-            searchBarSpaceHeightPx = tallSearchBarSpaceHeightPx;
-            searchBarBottomPaddingPx = tallSearchBarBottomPaddingPx;
-            searchBarTopExtraPaddingPx = isPhone ? tallSearchBarNegativeTopPaddingPx
-                    : normalSearchBarTopExtraPaddingPx;
-        } else {
-            hotseatBarHeightPx = normalHotseatBarHeightPx;
-            searchBarSpaceHeightPx = normalSearchBarSpaceHeightPx;
-            searchBarBottomPaddingPx = normalSearchBarBottomPaddingPx;
-            searchBarTopExtraPaddingPx = normalSearchBarTopExtraPaddingPx;
-        }
-    }
-
-    public void layout(Launcher launcher) {
+    public void layout(Launcher launcher, boolean notifyListeners) {
         FrameLayout.LayoutParams lp;
         boolean hasVerticalBarLayout = isVerticalBarLayout();
         final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
 
         // Layout the search bar space
-        Rect searchBarBounds = getSearchBarBounds(isLayoutRtl);
-        View searchBar = launcher.getSearchDropTargetBar();
+        Point searchBarBounds = getSearchBarDimensForWidgetOpts();
+        View searchBar = launcher.getDropTargetBar();
         lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
-        lp.width = searchBarBounds.width();
-        lp.height = searchBarBounds.height();
-        lp.topMargin = searchBarTopExtraPaddingPx;
-        if (hasVerticalBarLayout) {
-            // Vertical search bar space -- The search bar is fixed in the layout to be on the left
-            //                              of the screen regardless of RTL
-            lp.gravity = Gravity.LEFT;
-
-            LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar);
-            targets.setOrientation(LinearLayout.VERTICAL);
-            FrameLayout.LayoutParams targetsLp = (FrameLayout.LayoutParams) targets.getLayoutParams();
-            targetsLp.gravity = Gravity.TOP;
-            targetsLp.height = LayoutParams.WRAP_CONTENT;
-
-        } else {
-            // Horizontal search bar space
-            lp.gravity = Gravity.TOP|Gravity.CENTER_HORIZONTAL;
-        }
+        lp.width = searchBarBounds.x;
+        lp.height = searchBarBounds.y;
+        lp.topMargin = mInsets.top + edgeMarginPx;
         searchBar.setLayoutParams(lp);
 
         // Layout the workspace
         PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace);
-        lp = (FrameLayout.LayoutParams) workspace.getLayoutParams();
-        lp.gravity = Gravity.CENTER;
-        Rect padding = getWorkspacePadding(isLayoutRtl);
-        workspace.setLayoutParams(lp);
-        workspace.setPadding(padding.left, padding.top, padding.right, padding.bottom);
-        workspace.setPageSpacing(getWorkspacePageSpacing(isLayoutRtl));
+        Rect workspacePadding = getWorkspacePadding(null);
+        workspace.setPadding(workspacePadding.left, workspacePadding.top, workspacePadding.right,
+                workspacePadding.bottom);
+        workspace.setPageSpacing(getWorkspacePageSpacing());
+
+        View qsbContainer = launcher.getQsbContainer();
+        lp = (FrameLayout.LayoutParams) qsbContainer.getLayoutParams();
+        lp.topMargin = mInsets.top + workspacePadding.top;
+        qsbContainer.setLayoutParams(lp);
 
         // Layout the hotseat
-        View hotseat = launcher.findViewById(R.id.hotseat);
+        Hotseat hotseat = (Hotseat) launcher.findViewById(R.id.hotseat);
         lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
         // We want the edges of the hotseat to line up with the edges of the workspace, but the
         // icons in the hotseat are a different size, and so don't line up perfectly. To account for
@@ -496,83 +500,73 @@
             // Vertical hotseat -- The hotseat is fixed in the layout to be on the right of the
             //                     screen regardless of RTL
             lp.gravity = Gravity.RIGHT;
-            lp.width = normalHotseatBarHeightPx;
+            lp.width = hotseatBarHeightPx + mInsets.left + mInsets.right;
             lp.height = LayoutParams.MATCH_PARENT;
-            hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx);
+            hotseat.getLayout().setPadding(mInsets.left, mInsets.top, mInsets.right,
+                    workspacePadding.bottom);
         } else if (isTablet) {
             // Pad the hotseat with the workspace padding calculated above
             lp.gravity = Gravity.BOTTOM;
             lp.width = LayoutParams.MATCH_PARENT;
-            lp.height = hotseatBarHeightPx;
-            hotseat.findViewById(R.id.layout).setPadding(
-                    hotseatAdjustment + padding.left, 0,
-                    hotseatAdjustment + padding.right, 2 * edgeMarginPx);
+            lp.height = hotseatBarHeightPx + mInsets.bottom;
+            hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left,
+                    hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right,
+                    mInsets.bottom);
         } else {
             // For phones, layout the hotseat without any bottom margin
             // to ensure that we have space for the folders
             lp.gravity = Gravity.BOTTOM;
             lp.width = LayoutParams.MATCH_PARENT;
-            lp.height = hotseatBarHeightPx;
-            hotseat.findViewById(R.id.layout).setPadding(
-                    hotseatAdjustment + padding.left, 0,
-                    hotseatAdjustment + padding.right, 0);
+            lp.height = hotseatBarHeightPx + mInsets.bottom;
+            hotseat.getLayout().setPadding(hotseatAdjustment + workspacePadding.left,
+                    hotseatBarTopPaddingPx, hotseatAdjustment + workspacePadding.right,
+                    mInsets.bottom);
         }
         hotseat.setLayoutParams(lp);
 
         // Layout the page indicators
         View pageIndicator = launcher.findViewById(R.id.page_indicator);
         if (pageIndicator != null) {
-            if (hasVerticalBarLayout) {
-                // Hide the page indicators when we have vertical search/hotseat
-                pageIndicator.setVisibility(View.GONE);
+            lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
+            if (isVerticalBarLayout()) {
+                if (mInsets.left > 0) {
+                    lp.leftMargin = mInsets.left + pageIndicatorLandGutterLeftNavBarPx -
+                            lp.width - pageIndicatorLandWorkspaceOffsetPx;
+                } else if (mInsets.right > 0) {
+                    lp.leftMargin = pageIndicatorLandGutterRightNavBarPx - lp.width -
+                            pageIndicatorLandWorkspaceOffsetPx;
+                }
+                lp.bottomMargin = workspacePadding.bottom;
             } else {
                 // Put the page indicators above the hotseat
-                lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
                 lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-                lp.width = LayoutParams.WRAP_CONTENT;
-                lp.height = LayoutParams.WRAP_CONTENT;
-                lp.bottomMargin = hotseatBarHeightPx;
-                pageIndicator.setLayoutParams(lp);
+                lp.height = pageIndicatorHeightPx;
+                lp.bottomMargin = hotseatBarHeightPx + mInsets.bottom;
             }
+            pageIndicator.setLayoutParams(lp);
         }
 
         // Layout the Overview Mode
         ViewGroup overviewMode = launcher.getOverviewPanel();
         if (overviewMode != null) {
-            int overviewButtonBarHeight = getOverviewModeButtonBarHeight();
             lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams();
-            lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+            lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
 
             int visibleChildCount = getVisibleChildCount(overviewMode);
             int totalItemWidth = visibleChildCount * overviewModeBarItemWidthPx;
             int maxWidth = totalItemWidth + (visibleChildCount-1) * overviewModeBarSpacerWidthPx;
 
             lp.width = Math.min(availableWidthPx, maxWidth);
-            lp.height = overviewButtonBarHeight;
+            lp.height = getOverviewModeButtonBarHeight();
+            // Center the overview buttons on the workspace page
+            lp.leftMargin = workspacePadding.left + (availableWidthPx -
+                    workspacePadding.left - workspacePadding.right - lp.width) / 2;
             overviewMode.setLayoutParams(lp);
+        }
 
-            if (lp.width > totalItemWidth && visibleChildCount > 1) {
-                // We have enough space. Lets add some margin too.
-                int margin = (lp.width - totalItemWidth) / (visibleChildCount-1);
-                View lastChild = null;
-
-                // Set margin of all visible children except the last visible child
-                for (int i = 0; i < visibleChildCount; i++) {
-                    if (lastChild != null) {
-                        MarginLayoutParams clp = (MarginLayoutParams) lastChild.getLayoutParams();
-                        if (isLayoutRtl) {
-                            clp.leftMargin = margin;
-                        } else {
-                            clp.rightMargin = margin;
-                        }
-                        lastChild.setLayoutParams(clp);
-                        lastChild = null;
-                    }
-                    View thisChild = overviewMode.getChildAt(i);
-                    if (thisChild.getVisibility() != View.GONE) {
-                        lastChild = thisChild;
-                    }
-                }
+        if (notifyListeners) {
+            for (int i = mListeners.size() - 1; i >= 0; i--) {
+                mListeners.get(i).onLauncherLayoutChanged();
             }
         }
     }
@@ -588,4 +582,22 @@
                 ? Math.min(widthPx, heightPx)
                 : Math.max(widthPx, heightPx);
     }
+
+
+    /**
+     * @return the left/right paddings for all containers.
+     */
+    public final int[] getContainerPadding(Context context) {
+        Resources res = context.getResources();
+
+        // No paddings for portrait phone
+        if (isPhone && !isVerticalBarLayout()) {
+            return new int[] {0, 0};
+        }
+
+        // In landscape, we match the width of the workspace
+        int padding = (pageIndicatorLandGutterRightNavBarPx +
+                hotseatBarHeightPx + hotseatLandGutterPx + mInsets.left) / 2;
+        return new int[]{ padding, padding };
+    }
 }
diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java
index 2a1346e..efbb9d7 100644
--- a/src/com/android/launcher3/DragSource.java
+++ b/src/com/android/launcher3/DragSource.java
@@ -19,11 +19,12 @@
 import android.view.View;
 
 import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.logging.UserEventDispatcher.LaunchSourceProvider;
 
 /**
  * Interface defining an object that can originate a drag.
  */
-public interface DragSource {
+public interface DragSource extends LaunchSourceProvider {
 
     /**
      * @return whether items dragged from this source supports
@@ -37,7 +38,7 @@
 
     /**
      * @return whether items dragged from this source supports 'Delete' drop target (e.g. to remove
-     * a shortcut.
+     * a shortcut.) If this returns false, the drop target will say "Cancel" instead of "Remove."
      */
     boolean supportsDeleteDropTarget();
 
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index 4340591..efdeb1f 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3;
 
+import com.android.launcher3.dragndrop.DragView;
+
 import android.graphics.PointF;
 import android.graphics.Rect;
 
@@ -27,9 +29,7 @@
  */
 public interface DropTarget {
 
-    public static final String TAG = "DropTarget";
-
-    public static class DragObject {
+    class DragObject {
         public int x = -1;
         public int y = -1;
 
@@ -48,8 +48,11 @@
         /** The view that moves around while you drag.  */
         public DragView dragView = null;
 
-        /** The data associated with the object being dragged */
-        public Object dragInfo = null;
+        /** The data associated with the object, after item is dropped. */
+        public ItemInfo dragInfo = null;
+
+        /** The data associated with the object  being dragged */
+        public ItemInfo originalDragInfo = null;
 
         /** Where the drag originated */
         public DragSource dragSource = null;
@@ -152,7 +155,4 @@
 
     // These methods are implemented in Views
     void getHitRectRelativeToDragLayer(Rect outRect);
-    void getLocationInDragLayer(int[] loc);
-    int getLeft();
-    int getTop();
 }
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
new file mode 100644
index 0000000..42bab47
--- /dev/null
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewPropertyAnimator;
+import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AccelerateInterpolator;
+import android.widget.LinearLayout;
+
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+
+/*
+ * The top bar containing various drop targets: Delete/App Info/Uninstall.
+ */
+public class DropTargetBar extends LinearLayout implements DragController.DragListener {
+
+    protected static final int DEFAULT_DRAG_FADE_DURATION = 175;
+    protected static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator();
+
+    private final Runnable mFadeAnimationEndRunnable = new Runnable() {
+
+        @Override
+        public void run() {
+            AccessibilityManager am = (AccessibilityManager)
+                    getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+            boolean accessibilityEnabled = am.isEnabled();
+            AlphaUpdateListener.updateVisibility(DropTargetBar.this, accessibilityEnabled);
+        }
+    };
+
+    @ViewDebug.ExportedProperty(category = "launcher")
+    protected boolean mDeferOnDragEnd;
+
+    @ViewDebug.ExportedProperty(category = "launcher")
+    protected boolean mVisible = false;
+
+    private ViewPropertyAnimator mCurrentAnimation;
+
+    // Drop targets
+    private ButtonDropTarget mDeleteDropTarget;
+    private ButtonDropTarget mAppInfoDropTarget;
+    private ButtonDropTarget mUninstallDropTarget;
+
+    public DropTargetBar(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public DropTargetBar(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        // Get the individual components
+        mDeleteDropTarget = (ButtonDropTarget) findViewById(R.id.delete_target_text);
+        mAppInfoDropTarget = (ButtonDropTarget) findViewById(R.id.info_target_text);
+        mUninstallDropTarget = (ButtonDropTarget) findViewById(R.id.uninstall_target_text);
+
+        mDeleteDropTarget.setDropTargetBar(this);
+        mAppInfoDropTarget.setDropTargetBar(this);
+        mUninstallDropTarget.setDropTargetBar(this);
+
+        // Initialize with hidden state
+        setAlpha(0f);
+    }
+
+    public void setup(DragController dragController) {
+        dragController.addDragListener(this);
+        dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
+
+        dragController.addDragListener(mDeleteDropTarget);
+        dragController.addDragListener(mAppInfoDropTarget);
+        dragController.addDragListener(mUninstallDropTarget);
+
+        dragController.addDropTarget(mDeleteDropTarget);
+        dragController.addDropTarget(mAppInfoDropTarget);
+        dragController.addDropTarget(mUninstallDropTarget);
+    }
+
+    private void animateToVisibility(boolean isVisible) {
+        if (mVisible != isVisible) {
+            mVisible = isVisible;
+
+            // Cancel any existing animation
+            if (mCurrentAnimation != null) {
+                mCurrentAnimation.cancel();
+                mCurrentAnimation = null;
+            }
+
+            float finalAlpha = mVisible ? 1 : 0;
+            if (Float.compare(getAlpha(), finalAlpha) != 0) {
+                setVisibility(View.VISIBLE);
+                mCurrentAnimation = animate().alpha(finalAlpha)
+                        .setInterpolator(DEFAULT_INTERPOLATOR)
+                        .setDuration(DEFAULT_DRAG_FADE_DURATION)
+                        .withEndAction(mFadeAnimationEndRunnable);
+            }
+
+        }
+    }
+
+    /*
+     * DragController.DragListener implementation
+     */
+    @Override
+    public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+        animateToVisibility(true);
+    }
+
+    /**
+     * This is called to defer hiding the delete drop target until the drop animation has completed,
+     * instead of hiding immediately when the drag has ended.
+     */
+    protected void deferOnDragEnd() {
+        mDeferOnDragEnd = true;
+    }
+
+    @Override
+    public void onDragEnd() {
+        if (!mDeferOnDragEnd) {
+            animateToVisibility(false);
+        } else {
+            mDeferOnDragEnd = false;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index c7b64ec..c06f727 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -17,7 +17,9 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.DragEvent;
 import android.view.KeyEvent;
+import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
 
 
@@ -26,6 +28,8 @@
  */
 public class ExtendedEditText extends EditText {
 
+    private boolean mShowImeAfterFirstLayout;
+
     /**
      * Implemented by listeners of the back key.
      */
@@ -36,10 +40,12 @@
     private OnBackKeyListener mBackKeyListener;
 
     public ExtendedEditText(Context context) {
+        // ctor chaining breaks the touch handling
         super(context);
     }
 
     public ExtendedEditText(Context context, AttributeSet attrs) {
+        // ctor chaining breaks the touch handling
         super(context, attrs);
     }
 
@@ -62,4 +68,35 @@
         }
         return super.onKeyPreIme(keyCode, event);
     }
+
+    @Override
+    public boolean onDragEvent(DragEvent event) {
+        // We don't want this view to interfere with Launcher own drag and drop.
+        return false;
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (mShowImeAfterFirstLayout) {
+            // soft input only shows one frame after the layout of the EditText happens,
+            post(new Runnable() {
+                @Override
+                public void run() {
+                    showSoftInput();
+                    mShowImeAfterFirstLayout = false;
+                }
+            });
+        }
+    }
+
+    public void showKeyboard() {
+        mShowImeAfterFirstLayout = !showSoftInput();
+    }
+
+    private boolean showSoftInput() {
+        return requestFocus() &&
+                ((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE))
+                    .showSoftInput(this, InputMethodManager.SHOW_IMPLICIT);
+    }
 }
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index d7f1d86..3870080 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -93,7 +93,7 @@
     private static final ColorMatrix sTempBrightnessMatrix = new ColorMatrix();
     private static final ColorMatrix sTempFilterMatrix = new ColorMatrix();
 
-    private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+    private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
     private final Bitmap mBitmap;
     private State mState = State.NORMAL;
 
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index d59c644..789c3f9 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -22,6 +22,9 @@
 import android.view.View;
 import android.view.ViewGroup;
 
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderPagedView;
 import com.android.launcher3.util.FocusLogic;
 import com.android.launcher3.util.Thunk;
 
@@ -90,7 +93,7 @@
             }
 
             if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) {
-                if (LauncherAppState.isDogfoodBuild()) {
+                if (ProviderConfig.IS_DOGFOOD_BUILD) {
                     throw new IllegalStateException("Parent of the focused item is not supported.");
                 } else {
                     return false;
@@ -201,7 +204,7 @@
             return consume;
         }
 
-        final Launcher launcher = (Launcher) v.getContext();
+        final Launcher launcher = Launcher.getLauncher(v.getContext());
         final DeviceProfile profile = launcher.getDeviceProfile();
 
         if (DEBUG) {
@@ -236,14 +239,12 @@
 
         if (keyCode == KeyEvent.KEYCODE_DPAD_UP &&
                 !profile.isVerticalBarLayout()) {
-            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
-                    true /* hotseat horizontal */, profile.inv.hotseatAllAppsRank);
+            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
             iconIndex += iconParent.getChildCount();
             parent = iconParent;
         } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT &&
                 profile.isVerticalBarLayout()) {
-            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
-                    false /* hotseat horizontal */, profile.inv.hotseatAllAppsRank);
+            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
             iconIndex += iconParent.getChildCount();
             parent = iconParent;
         } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
@@ -340,7 +341,7 @@
             return consume;
         }
 
-        Launcher launcher = (Launcher) v.getContext();
+        Launcher launcher = Launcher.getLauncher(v.getContext());
         DeviceProfile profile = launcher.getDeviceProfile();
 
         if (DEBUG) {
@@ -353,7 +354,7 @@
         CellLayout iconLayout = (CellLayout) parent.getParent();
         final Workspace workspace = (Workspace) iconLayout.getParent();
         final ViewGroup dragLayer = (ViewGroup) workspace.getParent();
-        final ViewGroup tabs = (ViewGroup) dragLayer.findViewById(R.id.search_drop_target_bar);
+        final ViewGroup tabs = (ViewGroup) dragLayer.findViewById(R.id.drop_target_bar);
         final Hotseat hotseat = (Hotseat) dragLayer.findViewById(R.id.hotseat);
 
         final ItemInfo itemInfo = (ItemInfo) v.getTag();
@@ -369,12 +370,10 @@
         // to take a user to the hotseat. For other dpad navigation, do not use the matrix extended
         // with the hotseat.
         if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) {
-            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
-                    true /* horizontal */, profile.inv.hotseatAllAppsRank);
+            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
         } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
                 profile.isVerticalBarLayout()) {
-            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
-                    false /* horizontal */, profile.inv.hotseatAllAppsRank);
+            matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout, profile);
         } else if (isUninstallKeyChord(e)) {
             matrix = FocusLogic.createSparseMatrix(iconLayout);
             if (UninstallDropTarget.supportsDrop(launcher, itemInfo)) {
diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java
deleted file mode 100644
index 58b38eb..0000000
--- a/src/com/android/launcher3/FocusIndicatorView.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.util.Pair;
-import android.view.View;
-
-import com.android.launcher3.util.Thunk;
-
-public class FocusIndicatorView extends View implements View.OnFocusChangeListener {
-
-    // It can be any number >0. The view is resized using scaleX and scaleY.
-    static final int DEFAULT_LAYOUT_SIZE = 100;
-
-    private static final float MIN_VISIBLE_ALPHA = 0.2f;
-    private static final long ANIM_DURATION = 150;
-
-    private final int[] mIndicatorPos = new int[2];
-    private final int[] mTargetViewPos = new int[2];
-
-    private ObjectAnimator mCurrentAnimation;
-    private ViewAnimState mTargetState;
-
-    private View mLastFocusedView;
-    private boolean mInitiated;
-    private final OnFocusChangeListener mHideIndicatorOnFocusListener;
-
-    private Pair<View, Boolean> mPendingCall;
-
-    public FocusIndicatorView(Context context) {
-        this(context, null);
-    }
-
-    public FocusIndicatorView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setAlpha(0);
-        setBackgroundColor(getResources().getColor(R.color.focused_background));
-
-        mHideIndicatorOnFocusListener = new OnFocusChangeListener() {
-            @Override
-            public void onFocusChange(View v, boolean hasFocus) {
-                if (hasFocus) {
-                    endCurrentAnimation();
-                    setAlpha(0);
-                }
-            }
-        };
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-
-        // Redraw if it is already showing. This avoids a bug where the height changes by a small
-        // amount on connecting/disconnecting a bluetooth keyboard.
-        if (mLastFocusedView != null) {
-            mPendingCall = Pair.create(mLastFocusedView, Boolean.TRUE);
-            invalidate();
-        }
-    }
-
-    /**
-     * Sets the alpha of this FocusIndicatorView to 0 when a view with this listener receives focus.
-     */
-    public View.OnFocusChangeListener getHideIndicatorOnFocusListener() {
-        return mHideIndicatorOnFocusListener;
-    }
-
-    @Override
-    public void onFocusChange(View v, boolean hasFocus) {
-        mPendingCall = null;
-        if (!mInitiated && (getWidth() == 0)) {
-            // View not yet laid out. Wait until the view is ready to be drawn, so that be can
-            // get the location on screen.
-            mPendingCall = Pair.create(v, hasFocus);
-            invalidate();
-            return;
-        }
-
-        if (!mInitiated) {
-            // The parent view should always the a parent of the target view.
-            computeLocationRelativeToParent(this, (View) getParent(), mIndicatorPos);
-            mInitiated = true;
-        }
-
-        if (hasFocus) {
-            int indicatorWidth = getWidth();
-            int indicatorHeight = getHeight();
-
-            endCurrentAnimation();
-            ViewAnimState nextState = new ViewAnimState();
-            nextState.scaleX = v.getScaleX() * v.getWidth() / indicatorWidth;
-            nextState.scaleY = v.getScaleY() * v.getHeight() / indicatorHeight;
-
-            computeLocationRelativeToParent(v, (View) getParent(), mTargetViewPos);
-            nextState.x = mTargetViewPos[0] - mIndicatorPos[0] - (1 - nextState.scaleX) * indicatorWidth / 2;
-            nextState.y = mTargetViewPos[1] - mIndicatorPos[1] - (1 - nextState.scaleY) * indicatorHeight / 2;
-
-            if (getAlpha() > MIN_VISIBLE_ALPHA) {
-                mTargetState = nextState;
-                mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this,
-                        PropertyValuesHolder.ofFloat(View.ALPHA, 1),
-                        PropertyValuesHolder.ofFloat(View.TRANSLATION_X, mTargetState.x),
-                        PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, mTargetState.y),
-                        PropertyValuesHolder.ofFloat(View.SCALE_X, mTargetState.scaleX),
-                        PropertyValuesHolder.ofFloat(View.SCALE_Y, mTargetState.scaleY));
-            } else {
-                applyState(nextState);
-                mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this,
-                        PropertyValuesHolder.ofFloat(View.ALPHA, 1));
-            }
-            mLastFocusedView = v;
-        } else {
-            if (mLastFocusedView == v) {
-                mLastFocusedView = null;
-                endCurrentAnimation();
-                mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this,
-                        PropertyValuesHolder.ofFloat(View.ALPHA, 0));
-            }
-        }
-        if (mCurrentAnimation != null) {
-            mCurrentAnimation.setDuration(ANIM_DURATION).start();
-        }
-    }
-
-    private void endCurrentAnimation() {
-        if (mCurrentAnimation != null) {
-            mCurrentAnimation.cancel();
-            mCurrentAnimation = null;
-        }
-        if (mTargetState != null) {
-            applyState(mTargetState);
-            mTargetState = null;
-        }
-    }
-
-    private void applyState(ViewAnimState state) {
-        setTranslationX(state.x);
-        setTranslationY(state.y);
-        setScaleX(state.scaleX);
-        setScaleY(state.scaleY);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        if (mPendingCall != null) {
-            onFocusChange(mPendingCall.first, mPendingCall.second);
-        }
-    }
-
-    /**
-     * Computes the location of a view relative to {@param parent}, off-setting
-     * any shift due to page view scroll.
-     * @param pos an array of two integers in which to hold the coordinates
-     */
-    private static void computeLocationRelativeToParent(View v, View parent, int[] pos) {
-        pos[0] = pos[1] = 0;
-        computeLocationRelativeToParentHelper(v, parent, pos);
-
-        // If a view is scaled, its position will also shift accordingly. For optimization, only
-        // consider this for the last node.
-        pos[0] += (1 - v.getScaleX()) * v.getWidth() / 2;
-        pos[1] += (1 - v.getScaleY()) * v.getHeight() / 2;
-    }
-
-    private static void computeLocationRelativeToParentHelper(View child,
-            View commonParent, int[] shift) {
-        View parent = (View) child.getParent();
-        shift[0] += child.getLeft();
-        shift[1] += child.getTop();
-        if (parent instanceof PagedView) {
-            PagedView page = (PagedView) parent;
-            shift[0] -= page.getScrollForPage(page.indexOfChild(child));
-        }
-
-        if (parent != commonParent) {
-            computeLocationRelativeToParentHelper(parent, commonParent, shift);
-        }
-    }
-
-    @Thunk static final class ViewAnimState {
-        float x, y, scaleX, scaleY;
-    }
-}
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
deleted file mode 100644
index d7b55b3..0000000
--- a/src/com/android/launcher3/FolderIcon.java
+++ /dev/null
@@ -1,760 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.PorterDuff;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.os.Looper;
-import android.os.Parcelable;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.FolderInfo.FolderListener;
-import com.android.launcher3.util.Thunk;
-
-import java.util.ArrayList;
-
-/**
- * An icon that can appear on in the workspace representing an {@link UserFolder}.
- */
-public class FolderIcon extends FrameLayout implements FolderListener {
-    @Thunk Launcher mLauncher;
-    @Thunk Folder mFolder;
-    private FolderInfo mInfo;
-    @Thunk static boolean sStaticValuesDirty = true;
-
-    private CheckLongPressHelper mLongPressHelper;
-    private StylusEventHelper mStylusEventHelper;
-
-    // The number of icons to display in the
-    public static final int NUM_ITEMS_IN_PREVIEW = 3;
-    private static final int CONSUMPTION_ANIMATION_DURATION = 100;
-    private static final int DROP_IN_ANIMATION_DURATION = 400;
-    private static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
-    private static final int FINAL_ITEM_ANIMATION_DURATION = 200;
-
-    // The degree to which the inner ring grows when accepting drop
-    private static final float INNER_RING_GROWTH_FACTOR = 0.15f;
-
-    // The degree to which the outer ring is scaled in its natural state
-    private static final float OUTER_RING_GROWTH_FACTOR = 0.3f;
-
-    // The amount of vertical spread between items in the stack [0...1]
-    private static final float PERSPECTIVE_SHIFT_FACTOR = 0.18f;
-
-    // Flag as to whether or not to draw an outer ring. Currently none is designed.
-    public static final boolean HAS_OUTER_RING = true;
-
-    // Flag whether the folder should open itself when an item is dragged over is enabled.
-    public static final boolean SPRING_LOADING_ENABLED = true;
-
-    // The degree to which the item in the back of the stack is scaled [0...1]
-    // (0 means it's not scaled at all, 1 means it's scaled to nothing)
-    private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f;
-
-    // Delay when drag enters until the folder opens, in miliseconds.
-    private static final int ON_OPEN_DELAY = 800;
-
-    public static Drawable sSharedFolderLeaveBehind = null;
-
-    @Thunk ImageView mPreviewBackground;
-    @Thunk BubbleTextView mFolderName;
-
-    FolderRingAnimator mFolderRingAnimator = null;
-
-    // These variables are all associated with the drawing of the preview; they are stored
-    // as member variables for shared usage and to avoid computation on each frame
-    private int mIntrinsicIconSize;
-    private float mBaselineIconScale;
-    private int mBaselineIconSize;
-    private int mAvailableSpaceInPreview;
-    private int mTotalWidth = -1;
-    private int mPreviewOffsetX;
-    private int mPreviewOffsetY;
-    private float mMaxPerspectiveShift;
-    boolean mAnimating = false;
-    private Rect mOldBounds = new Rect();
-
-    private float mSlop;
-
-    private PreviewItemDrawingParams mParams = new PreviewItemDrawingParams(0, 0, 0, 0);
-    @Thunk PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0);
-    @Thunk ArrayList<ShortcutInfo> mHiddenItems = new ArrayList<ShortcutInfo>();
-
-    private Alarm mOpenAlarm = new Alarm();
-    @Thunk ItemInfo mDragInfo;
-
-    public FolderIcon(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init();
-    }
-
-    public FolderIcon(Context context) {
-        super(context);
-        init();
-    }
-
-    private void init() {
-        mLongPressHelper = new CheckLongPressHelper(this);
-        mStylusEventHelper = new StylusEventHelper(this);
-        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
-    }
-
-    public boolean isDropEnabled() {
-        final ViewGroup cellLayoutChildren = (ViewGroup) getParent();
-        final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent();
-        final Workspace workspace = (Workspace) cellLayout.getParent();
-        return !workspace.workspaceInModalState();
-    }
-
-    static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
-            FolderInfo folderInfo, IconCache iconCache) {
-        @SuppressWarnings("all") // suppress dead code warning
-        final boolean error = INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION;
-        if (error) {
-            throw new IllegalStateException("DROP_IN_ANIMATION_DURATION must be greater than " +
-                    "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " +
-                    "is dependent on this");
-        }
-
-        DeviceProfile grid = launcher.getDeviceProfile();
-
-        FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
-        icon.setClipToPadding(false);
-        icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
-        icon.mFolderName.setText(folderInfo.title);
-        icon.mFolderName.setCompoundDrawablePadding(0);
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams();
-        lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
-
-        // Offset the preview background to center this view accordingly
-        icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);
-        lp = (FrameLayout.LayoutParams) icon.mPreviewBackground.getLayoutParams();
-        lp.topMargin = grid.folderBackgroundOffset;
-        lp.width = grid.folderIconSizePx;
-        lp.height = grid.folderIconSizePx;
-
-        icon.setTag(folderInfo);
-        icon.setOnClickListener(launcher);
-        icon.mInfo = folderInfo;
-        icon.mLauncher = launcher;
-        icon.setContentDescription(String.format(launcher.getString(R.string.folder_name_format),
-                folderInfo.title));
-        Folder folder = Folder.fromXml(launcher);
-        folder.setDragController(launcher.getDragController());
-        folder.setFolderIcon(icon);
-        folder.bind(folderInfo);
-        icon.mFolder = folder;
-
-        icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon);
-        folderInfo.addListener(icon);
-
-        icon.setOnFocusChangeListener(launcher.mFocusHandler);
-        return icon;
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        sStaticValuesDirty = true;
-        return super.onSaveInstanceState();
-    }
-
-    public static class FolderRingAnimator {
-        public int mCellX;
-        public int mCellY;
-        @Thunk CellLayout mCellLayout;
-        public float mOuterRingSize;
-        public float mInnerRingSize;
-        public FolderIcon mFolderIcon = null;
-        public static Drawable sSharedOuterRingDrawable = null;
-        public static Drawable sSharedInnerRingDrawable = null;
-        public static int sPreviewSize = -1;
-        public static int sPreviewPadding = -1;
-
-        private ValueAnimator mAcceptAnimator;
-        private ValueAnimator mNeutralAnimator;
-
-        public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) {
-            mFolderIcon = folderIcon;
-            Resources res = launcher.getResources();
-
-            // We need to reload the static values when configuration changes in case they are
-            // different in another configuration
-            if (sStaticValuesDirty) {
-                if (Looper.myLooper() != Looper.getMainLooper()) {
-                    throw new RuntimeException("FolderRingAnimator loading drawables on non-UI thread "
-                            + Thread.currentThread());
-                }
-
-                DeviceProfile grid = launcher.getDeviceProfile();
-                sPreviewSize = grid.folderIconSizePx;
-                sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
-                sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer);
-                sSharedInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_nolip);
-                sSharedFolderLeaveBehind = res.getDrawable(R.drawable.portal_ring_rest);
-                sStaticValuesDirty = false;
-            }
-        }
-
-        public void animateToAcceptState() {
-            if (mNeutralAnimator != null) {
-                mNeutralAnimator.cancel();
-            }
-            mAcceptAnimator = LauncherAnimUtils.ofFloat(mCellLayout, 0f, 1f);
-            mAcceptAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
-
-            final int previewSize = sPreviewSize;
-            mAcceptAnimator.addUpdateListener(new AnimatorUpdateListener() {
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    final float percent = (Float) animation.getAnimatedValue();
-                    mOuterRingSize = (1 + percent * OUTER_RING_GROWTH_FACTOR) * previewSize;
-                    mInnerRingSize = (1 + percent * INNER_RING_GROWTH_FACTOR) * previewSize;
-                    if (mCellLayout != null) {
-                        mCellLayout.invalidate();
-                    }
-                }
-            });
-            mAcceptAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    if (mFolderIcon != null) {
-                        mFolderIcon.mPreviewBackground.setVisibility(INVISIBLE);
-                    }
-                }
-            });
-            mAcceptAnimator.start();
-        }
-
-        public void animateToNaturalState() {
-            if (mAcceptAnimator != null) {
-                mAcceptAnimator.cancel();
-            }
-            mNeutralAnimator = LauncherAnimUtils.ofFloat(mCellLayout, 0f, 1f);
-            mNeutralAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
-
-            final int previewSize = sPreviewSize;
-            mNeutralAnimator.addUpdateListener(new AnimatorUpdateListener() {
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    final float percent = (Float) animation.getAnimatedValue();
-                    mOuterRingSize = (1 + (1 - percent) * OUTER_RING_GROWTH_FACTOR) * previewSize;
-                    mInnerRingSize = (1 + (1 - percent) * INNER_RING_GROWTH_FACTOR) * previewSize;
-                    if (mCellLayout != null) {
-                        mCellLayout.invalidate();
-                    }
-                }
-            });
-            mNeutralAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (mCellLayout != null) {
-                        mCellLayout.hideFolderAccept(FolderRingAnimator.this);
-                    }
-                    if (mFolderIcon != null) {
-                        mFolderIcon.mPreviewBackground.setVisibility(VISIBLE);
-                    }
-                }
-            });
-            mNeutralAnimator.start();
-        }
-
-        // Location is expressed in window coordinates
-        public void getCell(int[] loc) {
-            loc[0] = mCellX;
-            loc[1] = mCellY;
-        }
-
-        // Location is expressed in window coordinates
-        public void setCell(int x, int y) {
-            mCellX = x;
-            mCellY = y;
-        }
-
-        public void setCellLayout(CellLayout layout) {
-            mCellLayout = layout;
-        }
-
-        public float getOuterRingSize() {
-            return mOuterRingSize;
-        }
-
-        public float getInnerRingSize() {
-            return mInnerRingSize;
-        }
-    }
-
-    public Folder getFolder() {
-        return mFolder;
-    }
-
-    FolderInfo getFolderInfo() {
-        return mInfo;
-    }
-
-    private boolean willAcceptItem(ItemInfo item) {
-        final int itemType = item.itemType;
-        return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
-                itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
-                !mFolder.isFull() && item != mInfo && !mInfo.opened);
-    }
-
-    public boolean acceptDrop(Object dragInfo) {
-        final ItemInfo item = (ItemInfo) dragInfo;
-        return !mFolder.isDestroyed() && willAcceptItem(item);
-    }
-
-    public void addItem(ShortcutInfo item) {
-        mInfo.add(item);
-    }
-
-    public void onDragEnter(Object dragInfo) {
-        if (mFolder.isDestroyed() || !willAcceptItem((ItemInfo) dragInfo)) return;
-        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
-        CellLayout layout = (CellLayout) getParent().getParent();
-        mFolderRingAnimator.setCell(lp.cellX, lp.cellY);
-        mFolderRingAnimator.setCellLayout(layout);
-        mFolderRingAnimator.animateToAcceptState();
-        layout.showFolderAccept(mFolderRingAnimator);
-        mOpenAlarm.setOnAlarmListener(mOnOpenListener);
-        if (SPRING_LOADING_ENABLED &&
-                ((dragInfo instanceof AppInfo) || (dragInfo instanceof ShortcutInfo))) {
-            // TODO: we currently don't support spring-loading for PendingAddShortcutInfos even
-            // though widget-style shortcuts can be added to folders. The issue is that we need
-            // to deal with configuration activities which are currently handled in
-            // Workspace#onDropExternal.
-            mOpenAlarm.setAlarm(ON_OPEN_DELAY);
-        }
-        mDragInfo = (ItemInfo) dragInfo;
-    }
-
-    public void onDragOver(Object dragInfo) {
-    }
-
-    OnAlarmListener mOnOpenListener = new OnAlarmListener() {
-        public void onAlarm(Alarm alarm) {
-            ShortcutInfo item;
-            if (mDragInfo instanceof AppInfo) {
-                // Came from all apps -- make a copy.
-                item = ((AppInfo) mDragInfo).makeShortcut();
-                item.spanX = 1;
-                item.spanY = 1;
-            } else {
-                // ShortcutInfo
-                item = (ShortcutInfo) mDragInfo;
-            }
-            mFolder.beginExternalDrag(item);
-            mLauncher.openFolder(FolderIcon.this);
-        }
-    };
-
-    public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
-            final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
-            float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
-
-        // These correspond two the drawable and view that the icon was dropped _onto_
-        Drawable animateDrawable = getTopDrawable((TextView) destView);
-        computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
-                destView.getMeasuredWidth());
-
-        // This will animate the first item from it's position as an icon into its
-        // position as the first item in the preview
-        animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION, false, null);
-        addItem(destInfo);
-
-        // This will animate the dragView (srcView) into the new folder
-        onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable, null);
-    }
-
-    public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) {
-        Drawable animateDrawable = getTopDrawable((TextView) finalView);
-        computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
-                finalView.getMeasuredWidth());
-
-        // This will animate the first item from it's position as an icon into its
-        // position as the first item in the preview
-        animateFirstItem(animateDrawable, FINAL_ITEM_ANIMATION_DURATION, true,
-                onCompleteRunnable);
-    }
-
-    public void onDragExit(Object dragInfo) {
-        onDragExit();
-    }
-
-    public void onDragExit() {
-        mFolderRingAnimator.animateToNaturalState();
-        mOpenAlarm.cancelAlarm();
-    }
-
-    private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
-            float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable,
-            DragObject d) {
-        item.cellX = -1;
-        item.cellY = -1;
-
-        // Typically, the animateView corresponds to the DragView; however, if this is being done
-        // after a configuration activity (ie. for a Shortcut being dragged from AllApps) we
-        // will not have a view to animate
-        if (animateView != null) {
-            DragLayer dragLayer = mLauncher.getDragLayer();
-            Rect from = new Rect();
-            dragLayer.getViewRectRelativeToSelf(animateView, from);
-            Rect to = finalRect;
-            if (to == null) {
-                to = new Rect();
-                Workspace workspace = mLauncher.getWorkspace();
-                // Set cellLayout and this to it's final state to compute final animation locations
-                workspace.setFinalTransitionTransform((CellLayout) getParent().getParent());
-                float scaleX = getScaleX();
-                float scaleY = getScaleY();
-                setScaleX(1.0f);
-                setScaleY(1.0f);
-                scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to);
-                // Finished computing final animation locations, restore current state
-                setScaleX(scaleX);
-                setScaleY(scaleY);
-                workspace.resetTransitionTransform((CellLayout) getParent().getParent());
-            }
-
-            int[] center = new int[2];
-            float scale = getLocalCenterForIndex(index, center);
-            center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
-            center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
-
-            to.offset(center[0] - animateView.getMeasuredWidth() / 2,
-                      center[1] - animateView.getMeasuredHeight() / 2);
-
-            float finalAlpha = index < NUM_ITEMS_IN_PREVIEW ? 0.5f : 0f;
-
-            float finalScale = scale * scaleRelativeToDragLayer;
-            dragLayer.animateView(animateView, from, to, finalAlpha,
-                    1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
-                    new DecelerateInterpolator(2), new AccelerateInterpolator(2),
-                    postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null);
-            addItem(item);
-            mHiddenItems.add(item);
-            mFolder.hideItem(item);
-            postDelayed(new Runnable() {
-                public void run() {
-                    mHiddenItems.remove(item);
-                    mFolder.showItem(item);
-                    invalidate();
-                }
-            }, DROP_IN_ANIMATION_DURATION);
-        } else {
-            addItem(item);
-        }
-    }
-
-    public void onDrop(DragObject d) {
-        ShortcutInfo item;
-        if (d.dragInfo instanceof AppInfo) {
-            // Came from all apps -- make a copy
-            item = ((AppInfo) d.dragInfo).makeShortcut();
-        } else {
-            item = (ShortcutInfo) d.dragInfo;
-        }
-        mFolder.notifyDrop();
-        onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, d);
-    }
-
-    private void computePreviewDrawingParams(int drawableSize, int totalSize) {
-        if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize) {
-            DeviceProfile grid = mLauncher.getDeviceProfile();
-
-            mIntrinsicIconSize = drawableSize;
-            mTotalWidth = totalSize;
-
-            final int previewSize = mPreviewBackground.getLayoutParams().height;
-            final int previewPadding = FolderRingAnimator.sPreviewPadding;
-
-            mAvailableSpaceInPreview = (previewSize - 2 * previewPadding);
-            // cos(45) = 0.707  + ~= 0.1) = 0.8f
-            int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f));
-
-            int unscaledHeight = (int) (mIntrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR));
-
-            mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight);
-
-            mBaselineIconSize = (int) (mIntrinsicIconSize * mBaselineIconScale);
-            mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR;
-
-            mPreviewOffsetX = (mTotalWidth - mAvailableSpaceInPreview) / 2;
-            mPreviewOffsetY = previewPadding + grid.folderBackgroundOffset;
-        }
-    }
-
-    private void computePreviewDrawingParams(Drawable d) {
-        computePreviewDrawingParams(d.getIntrinsicWidth(), getMeasuredWidth());
-    }
-
-    class PreviewItemDrawingParams {
-        PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
-            this.transX = transX;
-            this.transY = transY;
-            this.scale = scale;
-            this.overlayAlpha = overlayAlpha;
-        }
-        float transX;
-        float transY;
-        float scale;
-        float overlayAlpha;
-        Drawable drawable;
-    }
-
-    private float getLocalCenterForIndex(int index, int[] center) {
-        mParams = computePreviewItemDrawingParams(Math.min(NUM_ITEMS_IN_PREVIEW, index), mParams);
-
-        mParams.transX += mPreviewOffsetX;
-        mParams.transY += mPreviewOffsetY;
-        float offsetX = mParams.transX + (mParams.scale * mIntrinsicIconSize) / 2;
-        float offsetY = mParams.transY + (mParams.scale * mIntrinsicIconSize) / 2;
-
-        center[0] = (int) Math.round(offsetX);
-        center[1] = (int) Math.round(offsetY);
-        return mParams.scale;
-    }
-
-    private PreviewItemDrawingParams computePreviewItemDrawingParams(int index,
-            PreviewItemDrawingParams params) {
-        index = NUM_ITEMS_IN_PREVIEW - index - 1;
-        float r = (index * 1.0f) / (NUM_ITEMS_IN_PREVIEW - 1);
-        float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r));
-
-        float offset = (1 - r) * mMaxPerspectiveShift;
-        float scaledSize = scale * mBaselineIconSize;
-        float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize;
-
-        // We want to imagine our coordinates from the bottom left, growing up and to the
-        // right. This is natural for the x-axis, but for the y-axis, we have to invert things.
-        float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection) + getPaddingTop();
-        float transX = (mAvailableSpaceInPreview - scaledSize) / 2;
-        float totalScale = mBaselineIconScale * scale;
-        final float overlayAlpha = (80 * (1 - r)) / 255f;
-
-        if (params == null) {
-            params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
-        } else {
-            params.transX = transX;
-            params.transY = transY;
-            params.scale = totalScale;
-            params.overlayAlpha = overlayAlpha;
-        }
-        return params;
-    }
-
-    private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
-        canvas.save();
-        canvas.translate(params.transX + mPreviewOffsetX, params.transY + mPreviewOffsetY);
-        canvas.scale(params.scale, params.scale);
-        Drawable d = params.drawable;
-
-        if (d != null) {
-            mOldBounds.set(d.getBounds());
-            d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
-            if (d instanceof FastBitmapDrawable) {
-                FastBitmapDrawable fd = (FastBitmapDrawable) d;
-                float oldBrightness = fd.getBrightness();
-                fd.setBrightness(params.overlayAlpha);
-                d.draw(canvas);
-                fd.setBrightness(oldBrightness);
-            } else {
-                d.setColorFilter(Color.argb((int) (params.overlayAlpha * 255), 255, 255, 255),
-                        PorterDuff.Mode.SRC_ATOP);
-                d.draw(canvas);
-                d.clearColorFilter();
-            }
-            d.setBounds(mOldBounds);
-        }
-        canvas.restore();
-    }
-
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        super.dispatchDraw(canvas);
-
-        if (mFolder == null) return;
-        if (mFolder.getItemCount() == 0 && !mAnimating) return;
-
-        ArrayList<View> items = mFolder.getItemsInReadingOrder();
-        Drawable d;
-        TextView v;
-
-        // Update our drawing parameters if necessary
-        if (mAnimating) {
-            computePreviewDrawingParams(mAnimParams.drawable);
-        } else {
-            v = (TextView) items.get(0);
-            d = getTopDrawable(v);
-            computePreviewDrawingParams(d);
-        }
-
-        int nItemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW);
-        if (!mAnimating) {
-            for (int i = nItemsInPreview - 1; i >= 0; i--) {
-                v = (TextView) items.get(i);
-                if (!mHiddenItems.contains(v.getTag())) {
-                    d = getTopDrawable(v);
-                    mParams = computePreviewItemDrawingParams(i, mParams);
-                    mParams.drawable = d;
-                    drawPreviewItem(canvas, mParams);
-                }
-            }
-        } else {
-            drawPreviewItem(canvas, mAnimParams);
-        }
-    }
-
-    private Drawable getTopDrawable(TextView v) {
-        Drawable d = v.getCompoundDrawables()[1];
-        return (d instanceof PreloadIconDrawable) ? ((PreloadIconDrawable) d).mIcon : d;
-    }
-
-    private void animateFirstItem(final Drawable d, int duration, final boolean reverse,
-            final Runnable onCompleteRunnable) {
-        final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null);
-
-        float iconSize = mLauncher.getDeviceProfile().iconSizePx;
-        final float scale0 = iconSize / d.getIntrinsicWidth() ;
-        final float transX0 = (mAvailableSpaceInPreview - iconSize) / 2;
-        final float transY0 = (mAvailableSpaceInPreview - iconSize) / 2 + getPaddingTop();
-        mAnimParams.drawable = d;
-
-        ValueAnimator va = LauncherAnimUtils.ofFloat(this, 0f, 1.0f);
-        va.addUpdateListener(new AnimatorUpdateListener(){
-            public void onAnimationUpdate(ValueAnimator animation) {
-                float progress = (Float) animation.getAnimatedValue();
-                if (reverse) {
-                    progress = 1 - progress;
-                    mPreviewBackground.setAlpha(progress);
-                }
-
-                mAnimParams.transX = transX0 + progress * (finalParams.transX - transX0);
-                mAnimParams.transY = transY0 + progress * (finalParams.transY - transY0);
-                mAnimParams.scale = scale0 + progress * (finalParams.scale - scale0);
-                invalidate();
-            }
-        });
-        va.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mAnimating = true;
-            }
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mAnimating = false;
-                if (onCompleteRunnable != null) {
-                    onCompleteRunnable.run();
-                }
-            }
-        });
-        va.setDuration(duration);
-        va.start();
-    }
-
-    public void setTextVisible(boolean visible) {
-        if (visible) {
-            mFolderName.setVisibility(VISIBLE);
-        } else {
-            mFolderName.setVisibility(INVISIBLE);
-        }
-    }
-
-    public boolean getTextVisible() {
-        return mFolderName.getVisibility() == VISIBLE;
-    }
-
-    public void onItemsChanged() {
-        invalidate();
-        requestLayout();
-    }
-
-    public void onAdd(ShortcutInfo item) {
-        invalidate();
-        requestLayout();
-    }
-
-    public void onRemove(ShortcutInfo item) {
-        invalidate();
-        requestLayout();
-    }
-
-    public void onTitleChanged(CharSequence title) {
-        mFolderName.setText(title);
-        setContentDescription(String.format(getContext().getString(R.string.folder_name_format),
-                title));
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        // Call the superclass onTouchEvent first, because sometimes it changes the state to
-        // isPressed() on an ACTION_UP
-        boolean result = super.onTouchEvent(event);
-
-        // Check for a stylus button press, if it occurs cancel any long press checks.
-        if (mStylusEventHelper.checkAndPerformStylusEvent(event)) {
-            mLongPressHelper.cancelLongPress();
-            return true;
-        }
-
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_DOWN:
-                mLongPressHelper.postCheckForLongPress();
-                break;
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP:
-                mLongPressHelper.cancelLongPress();
-                break;
-            case MotionEvent.ACTION_MOVE:
-                if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
-                    mLongPressHelper.cancelLongPress();
-                }
-                break;
-        }
-        return result;
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
-    }
-
-    @Override
-    public void cancelLongPress() {
-        super.cancelLongPress();
-
-        mLongPressHelper.cancelLongPress();
-    }
-}
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index aea21c9..c0a8caa 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -22,7 +22,6 @@
 import com.android.launcher3.compat.UserHandleCompat;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 
 /**
  * Represents a folder containing shortcuts or apps.
@@ -49,7 +48,7 @@
     /**
      * Whether this folder has been opened
      */
-    boolean opened;
+    public boolean opened;
 
     public int options;
 
@@ -70,12 +69,12 @@
      *
      * @param item
      */
-    public void add(ShortcutInfo item) {
+    public void add(ShortcutInfo item, boolean animate) {
         contents.add(item);
         for (int i = 0; i < listeners.size(); i++) {
             listeners.get(i).onAdd(item);
         }
-        itemsChanged();
+        itemsChanged(animate);
     }
 
     /**
@@ -83,12 +82,12 @@
      *
      * @param item
      */
-    public void remove(ShortcutInfo item) {
+    public void remove(ShortcutInfo item, boolean animate) {
         contents.remove(item);
         for (int i = 0; i < listeners.size(); i++) {
             listeners.get(i).onRemove(item);
         }
-        itemsChanged();
+        itemsChanged(animate);
     }
 
     public void setTitle(CharSequence title) {
@@ -106,41 +105,25 @@
 
     }
 
-    void addListener(FolderListener listener) {
+    public void addListener(FolderListener listener) {
         listeners.add(listener);
     }
 
-    void removeListener(FolderListener listener) {
-        if (listeners.contains(listener)) {
-            listeners.remove(listener);
-        }
+    public void removeListener(FolderListener listener) {
+        listeners.remove(listener);
     }
 
-    void itemsChanged() {
+    public void itemsChanged(boolean animate) {
         for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).onItemsChanged();
+            listeners.get(i).onItemsChanged(animate);
         }
     }
 
-    @Override
-    void unbind() {
-        super.unbind();
-        listeners.clear();
-    }
-
-    interface FolderListener {
+    public interface FolderListener {
         public void onAdd(ShortcutInfo item);
         public void onRemove(ShortcutInfo item);
         public void onTitleChanged(CharSequence title);
-        public void onItemsChanged();
-    }
-
-    @Override
-    public String toString() {
-        return "FolderInfo(id=" + this.id + " type=" + this.itemType
-                + " container=" + this.container + " screen=" + screenId
-                + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
-                + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + ")";
+        public void onItemsChanged(boolean animate);
     }
 
     public boolean hasOption(int optionFlag) {
diff --git a/src/com/android/launcher3/HolographicOutlineHelper.java b/src/com/android/launcher3/HolographicOutlineHelper.java
index 0d68e33..9dec7d9 100644
--- a/src/com/android/launcher3/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/HolographicOutlineHelper.java
@@ -29,6 +29,10 @@
 import android.graphics.drawable.Drawable;
 import android.util.SparseArray;
 
+import com.android.launcher3.config.ProviderConfig;
+
+import java.nio.ByteBuffer;
+
 /**
  * Utility class to generate shadow and outline effect, which are used for click feedback
  * and drag-n-drop respectively.
@@ -38,14 +42,15 @@
     private static HolographicOutlineHelper sInstance;
 
     private final Canvas mCanvas = new Canvas();
-    private final Paint mDrawPaint = new Paint();
-    private final Paint mBlurPaint = new Paint();
-    private final Paint mErasePaint = new Paint();
+    private final Paint mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+    private final Paint mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+    private final Paint mErasePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
 
     private final BlurMaskFilter mMediumOuterBlurMaskFilter;
     private final BlurMaskFilter mThinOuterBlurMaskFilter;
     private final BlurMaskFilter mMediumInnerBlurMaskFilter;
 
+    private final float mShadowBitmapShift;
     private final BlurMaskFilter mShadowBlurMaskFilter;
 
     // We have 4 different icon sizes: homescreen, hotseat, folder & all-apps
@@ -61,16 +66,10 @@
         mThinOuterBlurMaskFilter = new BlurMaskFilter(
                 res.getDimension(R.dimen.blur_size_thin_outline), BlurMaskFilter.Blur.OUTER);
 
-        mShadowBlurMaskFilter = new BlurMaskFilter(
-                res.getDimension(R.dimen.blur_size_click_shadow), BlurMaskFilter.Blur.NORMAL);
+        mShadowBitmapShift = res.getDimension(R.dimen.blur_size_click_shadow);
+        mShadowBlurMaskFilter = new BlurMaskFilter(mShadowBitmapShift, BlurMaskFilter.Blur.NORMAL);
 
-        mDrawPaint.setFilterBitmap(true);
-        mDrawPaint.setAntiAlias(true);
-        mBlurPaint.setFilterBitmap(true);
-        mBlurPaint.setAntiAlias(true);
         mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
-        mErasePaint.setFilterBitmap(true);
-        mErasePaint.setAntiAlias(true);
     }
 
     public static HolographicOutlineHelper obtain(Context context) {
@@ -84,49 +83,53 @@
      * Applies a more expensive and accurate outline to whatever is currently drawn in a specified
      * bitmap.
      */
-    void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
-            int outlineColor) {
-        applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, true);
+    public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas) {
+        applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, true);
     }
-    void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
-            int outlineColor, boolean clipAlpha) {
+
+    public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas,
+            boolean clipAlpha) {
+        if (ProviderConfig.IS_DOGFOOD_BUILD && srcDst.getConfig() != Bitmap.Config.ALPHA_8) {
+            throw new RuntimeException("Outline blue is only supported on alpha bitmaps");
+        }
 
         // We start by removing most of the alpha channel so as to ignore shadows, and
         // other types of partial transparency when defining the shape of the object
         if (clipAlpha) {
-            int[] srcBuffer = new int[srcDst.getWidth() * srcDst.getHeight()];
-            srcDst.getPixels(srcBuffer,
-                    0, srcDst.getWidth(), 0, 0, srcDst.getWidth(), srcDst.getHeight());
-            for (int i = 0; i < srcBuffer.length; i++) {
-                final int alpha = srcBuffer[i] >>> 24;
-                if (alpha < 188) {
-                    srcBuffer[i] = 0;
+            byte[] pixels = new byte[srcDst.getWidth() * srcDst.getHeight()];
+            ByteBuffer buffer = ByteBuffer.wrap(pixels);
+            buffer.rewind();
+            srcDst.copyPixelsToBuffer(buffer);
+
+            for (int i = 0; i < pixels.length; i++) {
+                if ((pixels[i] & 0xFF) < 188) {
+                    pixels[i] = 0;
                 }
             }
-            srcDst.setPixels(srcBuffer,
-                    0, srcDst.getWidth(), 0, 0, srcDst.getWidth(), srcDst.getHeight());
+
+            buffer.rewind();
+            srcDst.copyPixelsFromBuffer(buffer);
         }
-        Bitmap glowShape = srcDst.extractAlpha();
 
         // calculate the outer blur first
         mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
         int[] outerBlurOffset = new int[2];
-        Bitmap thickOuterBlur = glowShape.extractAlpha(mBlurPaint, outerBlurOffset);
+        Bitmap thickOuterBlur = srcDst.extractAlpha(mBlurPaint, outerBlurOffset);
 
         mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
         int[] brightOutlineOffset = new int[2];
-        Bitmap brightOutline = glowShape.extractAlpha(mBlurPaint, brightOutlineOffset);
+        Bitmap brightOutline = srcDst.extractAlpha(mBlurPaint, brightOutlineOffset);
 
         // calculate the inner blur
-        srcDstCanvas.setBitmap(glowShape);
+        srcDstCanvas.setBitmap(srcDst);
         srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
         mBlurPaint.setMaskFilter(mMediumInnerBlurMaskFilter);
         int[] thickInnerBlurOffset = new int[2];
-        Bitmap thickInnerBlur = glowShape.extractAlpha(mBlurPaint, thickInnerBlurOffset);
+        Bitmap thickInnerBlur = srcDst.extractAlpha(mBlurPaint, thickInnerBlurOffset);
 
         // mask out the inner blur
         srcDstCanvas.setBitmap(thickInnerBlur);
-        srcDstCanvas.drawBitmap(glowShape, -thickInnerBlurOffset[0],
+        srcDstCanvas.drawBitmap(srcDst, -thickInnerBlurOffset[0],
                 -thickInnerBlurOffset[1], mErasePaint);
         srcDstCanvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(),
                 mErasePaint);
@@ -136,14 +139,12 @@
         // draw the inner and outer blur
         srcDstCanvas.setBitmap(srcDst);
         srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
-        mDrawPaint.setColor(color);
         srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
                 mDrawPaint);
         srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1],
                 mDrawPaint);
 
         // draw the bright outline
-        mDrawPaint.setColor(outlineColor);
         srcDstCanvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1],
                 mDrawPaint);
 
@@ -152,41 +153,75 @@
         brightOutline.recycle();
         thickOuterBlur.recycle();
         thickInnerBlur.recycle();
-        glowShape.recycle();
     }
 
     Bitmap createMediumDropShadow(BubbleTextView view) {
-        Drawable icon = view.getIcon();
-        if (icon == null) {
+        return createMediumDropShadow(view.getIcon(), view.getScaleX(), view.getScaleY(), true);
+    }
+
+    Bitmap createMediumDropShadow(Drawable drawable, boolean shouldCache) {
+        return createMediumDropShadow(drawable, 1f, 1f, shouldCache);
+    }
+
+    Bitmap createMediumDropShadow(Drawable drawable, float scaleX, float scaleY,
+                boolean shouldCache) {
+        if (drawable == null) {
             return null;
         }
-        Rect rect = icon.getBounds();
+        Rect rect = drawable.getBounds();
 
-        int bitmapWidth = (int) (rect.width() * view.getScaleX());
-        int bitmapHeight = (int) (rect.height() * view.getScaleY());
+        int bitmapWidth = (int) (rect.width() * scaleX);
+        int bitmapHeight = (int) (rect.height() * scaleY);
         if (bitmapHeight <= 0 || bitmapWidth <= 0) {
             return null;
         }
 
         int key = (bitmapWidth << 16) | bitmapHeight;
-        Bitmap cache = mBitmapCache.get(key);
+        Bitmap cache = shouldCache ? mBitmapCache.get(key) : null;
         if (cache == null) {
-            cache = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
+            cache = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ALPHA_8);
             mCanvas.setBitmap(cache);
-            mBitmapCache.put(key, cache);
+
+            if (shouldCache) {
+                mBitmapCache.put(key, cache);
+            }
         } else {
             mCanvas.setBitmap(cache);
-            mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
+            mCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
         }
 
-        mCanvas.save(Canvas.MATRIX_SAVE_FLAG);
-        mCanvas.scale(view.getScaleX(), view.getScaleY());
+        int saveCount = mCanvas.save();
+        mCanvas.scale(scaleX, scaleY);
         mCanvas.translate(-rect.left, -rect.top);
-        icon.draw(mCanvas);
-        mCanvas.restore();
+        drawable.draw(mCanvas);
+        mCanvas.restoreToCount(saveCount);
         mCanvas.setBitmap(null);
 
         mBlurPaint.setMaskFilter(mShadowBlurMaskFilter);
-        return cache.extractAlpha(mBlurPaint, null);
+
+        int extraSize = (int) (2 * mShadowBitmapShift);
+
+        int resultWidth = bitmapWidth + extraSize;
+        int resultHeight = bitmapHeight + extraSize;
+        key = (resultWidth << 16) | resultHeight;
+        Bitmap result = shouldCache ? mBitmapCache.get(key) : null;
+        if (result == null) {
+            result = Bitmap.createBitmap(resultWidth, resultHeight, Bitmap.Config.ALPHA_8);
+            mCanvas.setBitmap(result);
+        } else {
+            // Use put instead of delete, to avoid unnecessary shrinking of cache array
+            mBitmapCache.put(key, null);
+            mCanvas.setBitmap(result);
+            mCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
+        }
+        mCanvas.drawBitmap(cache, mShadowBitmapShift, mShadowBitmapShift, mBlurPaint);
+        mCanvas.setBitmap(null);
+        return result;
+    }
+
+    public void recycleShadowBitmap(Bitmap bitmap) {
+        if (bitmap != null) {
+            mBitmapCache.put((bitmap.getWidth() << 16) | bitmap.getHeight(), bitmap);
+        }
     }
 }
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 3e83876..0fbbc19 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -16,28 +16,47 @@
 
 package com.android.launcher3;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ArgbEvaluator;
+import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Color;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.os.Bundle;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.ColorUtils;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewDebug;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dynamicui.ExtractedColors;
+import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+
 public class Hotseat extends FrameLayout
-        implements Stats.LaunchSourceProvider{
+        implements UserEventDispatcher.LaunchSourceProvider {
 
     private CellLayout mContent;
 
     private Launcher mLauncher;
 
-    private int mAllAppsButtonRank;
-
+    @ViewDebug.ExportedProperty(category = "launcher")
     private final boolean mHasVerticalHotseat;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private int mBackgroundColor;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private ColorDrawable mBackground;
+    private ValueAnimator mBackgroundColorAnimator;
+
     public Hotseat(Context context) {
         this(context, null);
     }
@@ -48,11 +67,15 @@
 
     public Hotseat(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
         mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout();
+        mBackgroundColor = ColorUtils.setAlphaComponent(
+                ContextCompat.getColor(context, R.color.all_apps_container_color), 0);
+        mBackground = new ColorDrawable(mBackgroundColor);
+        setBackground(mBackground);
     }
 
-    CellLayout getLayout() {
+    public CellLayout getLayout() {
         return mContent;
     }
 
@@ -85,16 +108,10 @@
         return mHasVerticalHotseat ? (mContent.getCountY() - (rank + 1)) : 0;
     }
 
-    public boolean isAllAppsButtonRank(int rank) {
-        return rank == mAllAppsButtonRank;
-    }
-
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         DeviceProfile grid = mLauncher.getDeviceProfile();
-
-        mAllAppsButtonRank = grid.inv.hotseatAllAppsRank;
         mContent = (CellLayout) findViewById(R.id.layout);
         if (grid.isLandscape && !grid.isLargeTablet) {
             mContent.setGridSize(1, (int) grid.inv.numHotseatIcons);
@@ -109,52 +126,96 @@
     void resetLayout() {
         mContent.removeAllViewsInLayout();
 
-        // Add the Apps button
-        Context context = getContext();
+        if (!FeatureFlags.NO_ALL_APPS_ICON) {
+            // Add the Apps button
+            Context context = getContext();
+            int allAppsButtonRank = mLauncher.getDeviceProfile().inv.getAllAppsButtonRank();
 
-        LayoutInflater inflater = LayoutInflater.from(context);
-        TextView allAppsButton = (TextView)
-                inflater.inflate(R.layout.all_apps_button, mContent, false);
-        Drawable d = context.getResources().getDrawable(R.drawable.all_apps_button_icon);
+            LayoutInflater inflater = LayoutInflater.from(context);
+            TextView allAppsButton = (TextView)
+                    inflater.inflate(R.layout.all_apps_button, mContent, false);
+            Drawable d = context.getResources().getDrawable(R.drawable.all_apps_button_icon);
 
-        mLauncher.resizeIconDrawable(d);
-        int scaleDownPx = getResources().getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
-        Rect bounds = d.getBounds();
-        d.setBounds(bounds.left, bounds.top + scaleDownPx / 2, bounds.right - scaleDownPx,
-                bounds.bottom - scaleDownPx / 2);
-        allAppsButton.setCompoundDrawables(null, d, null, null);
+            mLauncher.resizeIconDrawable(d);
+            int scaleDownPx = getResources().getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
+            Rect bounds = d.getBounds();
+            d.setBounds(bounds.left, bounds.top + scaleDownPx / 2, bounds.right - scaleDownPx,
+                    bounds.bottom - scaleDownPx / 2);
+            allAppsButton.setCompoundDrawables(null, d, null, null);
 
-        allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));
-        allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener());
-        if (mLauncher != null) {
-            mLauncher.setAllAppsButton(allAppsButton);
-            allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
-            allAppsButton.setOnClickListener(mLauncher);
-            allAppsButton.setOnLongClickListener(mLauncher);
-            allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
+            allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));
+            allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener());
+            if (mLauncher != null) {
+                mLauncher.setAllAppsButton(allAppsButton);
+                allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
+                allAppsButton.setOnClickListener(mLauncher);
+                allAppsButton.setOnLongClickListener(mLauncher);
+                allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
+            }
+
+            // Note: We do this to ensure that the hotseat is always laid out in the orientation of
+            // the hotseat in order regardless of which orientation they were added
+            int x = getCellXFromOrder(allAppsButtonRank);
+            int y = getCellYFromOrder(allAppsButtonRank);
+            CellLayout.LayoutParams lp = new CellLayout.LayoutParams(x, y, 1, 1);
+            lp.canReorder = false;
+            mContent.addViewToCellLayout(allAppsButton, -1, allAppsButton.getId(), lp, true);
         }
-
-        // Note: We do this to ensure that the hotseat is always laid out in the orientation of
-        // the hotseat in order regardless of which orientation they were added
-        int x = getCellXFromOrder(mAllAppsButtonRank);
-        int y = getCellYFromOrder(mAllAppsButtonRank);
-        CellLayout.LayoutParams lp = new CellLayout.LayoutParams(x,y,1,1);
-        lp.canReorder = false;
-        mContent.addViewToCellLayout(allAppsButton, -1, allAppsButton.getId(), lp, true);
     }
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         // We don't want any clicks to go through to the hotseat unless the workspace is in
-        // the normal state.
-        if (mLauncher.getWorkspace().workspaceInModalState()) {
-            return true;
-        }
-        return false;
+        // the normal state or an accessible drag is in progress.
+        return mLauncher.getWorkspace().workspaceInModalState() &&
+                !mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
     }
 
     @Override
-    public void fillInLaunchSourceData(View v, Bundle sourceData) {
-        sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_HOTSEAT);
+    public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
+        target.gridX = info.cellX;
+        target.gridY = info.cellY;
+        targetParent.containerType = LauncherLogProto.HOTSEAT;
+    }
+
+    public void updateColor(ExtractedColors extractedColors, boolean animate) {
+        if (!mHasVerticalHotseat) {
+            int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX, Color.TRANSPARENT);
+            if (mBackgroundColorAnimator != null) {
+                mBackgroundColorAnimator.cancel();
+            }
+            if (!animate) {
+                setBackgroundColor(color);
+            } else {
+                mBackgroundColorAnimator = ValueAnimator.ofInt(mBackgroundColor, color);
+                mBackgroundColorAnimator.setEvaluator(new ArgbEvaluator());
+                mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                    @Override
+                    public void onAnimationUpdate(ValueAnimator animation) {
+                        mBackground.setColor((Integer) animation.getAnimatedValue());
+                    }
+                });
+                mBackgroundColorAnimator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mBackgroundColorAnimator = null;
+                    }
+                });
+                mBackgroundColorAnimator.start();
+            }
+            mBackgroundColor = color;
+        }
+    }
+
+    public void setBackgroundTransparent(boolean enable) {
+        if (enable) {
+            mBackground.setAlpha(0);
+        } else {
+            mBackground.setAlpha(255);
+        }
+    }
+
+    public int getBackgroundDrawableColor() {
+        return mBackgroundColor;
     }
 }
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index c72c2f9..5c86b6b 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -26,6 +26,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteException;
@@ -55,7 +56,6 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Locale;
 import java.util.Set;
 import java.util.Stack;
 
@@ -72,6 +72,7 @@
     private static final String EMPTY_CLASS_NAME = ".";
 
     private static final boolean DEBUG = false;
+    private static final boolean DEBUG_IGNORE_CACHE = false;
 
     private static final int LOW_RES_SCALE_FACTOR = 5;
 
@@ -89,6 +90,7 @@
 
     private final Context mContext;
     private final PackageManager mPackageManager;
+    private IconProvider mIconProvider;
     @Thunk final UserManagerCompat mUserManager;
     private final LauncherAppsCompat mLauncherApps;
     private final HashMap<ComponentKey, CacheEntry> mCache =
@@ -107,8 +109,6 @@
     private final int mPackageBgColor;
     private final BitmapFactory.Options mLowResOptions;
 
-    private String mSystemState;
-    private Bitmap mLowResBitmap;
     private Canvas mLowResCanvas;
     private Paint mLowResPaint;
 
@@ -119,16 +119,22 @@
         mLauncherApps = LauncherAppsCompat.getInstance(mContext);
         mIconDpi = inv.fillResIconDpi;
         mIconDb = new IconDB(context, inv.iconBitmapSize);
+        mLowResCanvas = new Canvas();
+        mLowResPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
+
+        mIconProvider = IconProvider.loadByName(context.getString(R.string.icon_provider_class),
+                context);
 
         mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
 
         mActivityBgColor = context.getResources().getColor(R.color.quantum_panel_bg_color);
-        mPackageBgColor = context.getResources().getColor(R.color.quantum_panel_bg_color_dark);
+        TypedArray ta = context.obtainStyledAttributes(new int[]{R.attr.colorSecondary});
+        mPackageBgColor = ta.getColor(0, 0);
+        ta.recycle();
         mLowResOptions = new BitmapFactory.Options();
         // Always prefer RGB_565 config for low res. If the bitmap has transparency, it will
         // automatically be loaded as ALPHA_8888.
         mLowResOptions.inPreferredConfig = Bitmap.Config.RGB_565;
-        updateSystemStateString();
     }
 
     private Drawable getFullResDefaultActivityIcon() {
@@ -240,7 +246,7 @@
         // Remove all active icon update tasks.
         mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN);
 
-        updateSystemStateString();
+        mIconProvider.updateSystemStateString();
         for (UserHandleCompat user : mUserManager.getUserProfiles()) {
             // Query for the set of apps
             final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
@@ -314,7 +320,8 @@
                 int version = c.getInt(indexVersion);
                 LauncherActivityInfoCompat app = componentMap.remove(component);
                 if (version == info.versionCode && updateTime == info.lastUpdateTime &&
-                        TextUtils.equals(mSystemState, c.getString(systemStateIndex))) {
+                        TextUtils.equals(c.getString(systemStateIndex),
+                                mIconProvider.getIconSystemState(info.packageName))) {
                     continue;
                 }
                 if (app == null) {
@@ -381,13 +388,16 @@
         if (entry == null) {
             entry = new CacheEntry();
             entry.icon = Utilities.createBadgedIconBitmap(
-                    app.getIcon(mIconDpi), app.getUser(), mContext);
+                    mIconProvider.getIcon(app, mIconDpi), app.getUser(),
+                    mContext);
         }
         entry.title = app.getLabel();
         entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
         mCache.put(new ComponentKey(app.getComponentName(), app.getUser()), entry);
 
-        return newContentValues(entry.icon, entry.title.toString(), mActivityBgColor);
+        Bitmap lowResIcon = generateLowResIcon(entry.icon, mActivityBgColor);
+        return newContentValues(entry.icon, lowResIcon, entry.title.toString(),
+                app.getApplicationInfo().packageName);
     }
 
     /**
@@ -408,7 +418,7 @@
                             st.user, false);
                 } else if (info instanceof PackageItemInfo) {
                     PackageItemInfo pti = (PackageItemInfo) info;
-                    getTitleAndIconForApp(pti.packageName, pti.user, false, pti);
+                    getTitleAndIconForApp(pti, false);
                 }
                 mMainThreadExecutor.execute(new Runnable() {
 
@@ -436,8 +446,8 @@
         CacheEntry entry = cacheLocked(application.componentName, info, user,
                 false, useLowResIcon);
         application.title = Utilities.trim(entry.title);
-        application.iconBitmap = getNonNullIcon(entry, user);
         application.contentDescription = entry.contentDescription;
+        application.iconBitmap = getNonNullIcon(entry, user);
         application.usingLowResIcon = entry.isLowResIcon;
     }
 
@@ -449,8 +459,8 @@
                 false, application.usingLowResIcon);
         if (entry.icon != null && !isDefaultIcon(entry.icon, application.user)) {
             application.title = Utilities.trim(entry.title);
-            application.iconBitmap = entry.icon;
             application.contentDescription = entry.contentDescription;
+            application.iconBitmap = entry.icon;
             application.usingLowResIcon = entry.isLowResIcon;
         }
     }
@@ -483,6 +493,7 @@
         if (component == null) {
             shortcutInfo.setIcon(getDefaultIcon(user));
             shortcutInfo.title = "";
+            shortcutInfo.contentDescription = "";
             shortcutInfo.usingFallbackIcon = true;
             shortcutInfo.usingLowResIcon = false;
         } else {
@@ -500,21 +511,22 @@
         CacheEntry entry = cacheLocked(component, info, user, usePkgIcon, useLowResIcon);
         shortcutInfo.setIcon(getNonNullIcon(entry, user));
         shortcutInfo.title = Utilities.trim(entry.title);
+        shortcutInfo.contentDescription = entry.contentDescription;
         shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user);
         shortcutInfo.usingLowResIcon = entry.isLowResIcon;
     }
 
     /**
-     * Fill in {@param appInfo} with the icon and label for {@param packageName}
+     * Fill in {@param infoInOut} with the corresponding icon and label.
      */
     public synchronized void getTitleAndIconForApp(
-            String packageName, UserHandleCompat user, boolean useLowResIcon,
-            PackageItemInfo infoOut) {
-        CacheEntry entry = getEntryForPackageLocked(packageName, user, useLowResIcon);
-        infoOut.iconBitmap = getNonNullIcon(entry, user);
-        infoOut.title = Utilities.trim(entry.title);
-        infoOut.usingLowResIcon = entry.isLowResIcon;
-        infoOut.contentDescription = entry.contentDescription;
+            PackageItemInfo infoInOut, boolean useLowResIcon) {
+        CacheEntry entry = getEntryForPackageLocked(
+                infoInOut.packageName, infoInOut.user, useLowResIcon);
+        infoInOut.title = Utilities.trim(entry.title);
+        infoInOut.contentDescription = entry.contentDescription;
+        infoInOut.iconBitmap = getNonNullIcon(entry, infoInOut.user);
+        infoInOut.usingLowResIcon = entry.isLowResIcon;
     }
 
     public synchronized Bitmap getDefaultIcon(UserHandleCompat user) {
@@ -541,10 +553,11 @@
             mCache.put(cacheKey, entry);
 
             // Check the DB first.
-            if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
+            if (!getEntryFromDB(cacheKey, entry, useLowResIcon) || DEBUG_IGNORE_CACHE) {
                 if (info != null) {
                     entry.icon = Utilities.createBadgedIconBitmap(
-                            info.getIcon(mIconDpi), info.getUser(), mContext);
+                            mIconProvider.getIcon(info, mIconDpi), info.getUser(),
+                            mContext);
                 } else {
                     if (usePackageIcon) {
                         CacheEntry packageEntry = getEntryForPackageLocked(
@@ -625,16 +638,21 @@
                     if (appInfo == null) {
                         throw new NameNotFoundException("ApplicationInfo is null");
                     }
-                    entry.icon = Utilities.createBadgedIconBitmap(
+
+                    // Load the full res icon for the application, but if useLowResIcon is set, then
+                    // only keep the low resolution icon instead of the larger full-sized icon
+                    Bitmap icon = Utilities.createBadgedIconBitmap(
                             appInfo.loadIcon(mPackageManager), user, mContext);
+                    Bitmap lowResIcon =  generateLowResIcon(icon, mPackageBgColor);
                     entry.title = appInfo.loadLabel(mPackageManager);
                     entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
-                    entry.isLowResIcon = false;
+                    entry.icon = useLowResIcon ? lowResIcon : icon;
+                    entry.isLowResIcon = useLowResIcon;
 
                     // Add the icon in the DB here, since these do not get written during
                     // package updates.
                     ContentValues values =
-                            newContentValues(entry.icon, entry.title.toString(), mPackageBgColor);
+                            newContentValues(icon, lowResIcon, entry.title.toString(), packageName);
                     addIconToDB(values, cacheKey.componentName, info,
                             mUserManager.getSerialNumberForUser(user));
 
@@ -674,9 +692,10 @@
             // pass
         }
 
-        ContentValues values = newContentValues(
-                Bitmap.createScaledBitmap(icon, idp.iconBitmapSize, idp.iconBitmapSize, true),
-                label, Color.TRANSPARENT);
+        icon = Bitmap.createScaledBitmap(icon, idp.iconBitmapSize, idp.iconBitmapSize, true);
+        Bitmap lowResIcon = generateLowResIcon(icon, Color.TRANSPARENT);
+        ContentValues values = newContentValues(icon, lowResIcon, label,
+                componentName.getPackageName());
         values.put(IconDB.COLUMN_COMPONENT, componentName.flattenToString());
         values.put(IconDB.COLUMN_USER, userSerial);
         mIconDb.insertOrReplace(values);
@@ -753,13 +772,15 @@
         public void run() {
             if (!mAppsToUpdate.isEmpty()) {
                 LauncherActivityInfoCompat app = mAppsToUpdate.pop();
-                String cn = app.getComponentName().flattenToString();
-                ContentValues values = updateCacheAndGetContentValues(app, true);
-                mIconDb.update(values,
-                        IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
-                        new String[]{cn, Long.toString(mUserSerial)});
-                mUpdatedPackages.add(app.getComponentName().getPackageName());
-
+                String pkg = app.getComponentName().getPackageName();
+                PackageInfo info = mPkgInfoMap.get(pkg);
+                if (info != null) {
+                    synchronized (IconCache.this) {
+                        ContentValues values = updateCacheAndGetContentValues(app, true);
+                        addIconToDB(values, app.getComponentName(), info, mUserSerial);
+                    }
+                    mUpdatedPackages.add(pkg);
+                }
                 if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) {
                     // No more app to update. Notify model.
                     LauncherAppState.getInstance().getModel().onPackageIconsUpdated(
@@ -788,15 +809,11 @@
         }
     }
 
-    private void updateSystemStateString() {
-        mSystemState = Locale.getDefault().toString();
-    }
-
     private static final class IconDB extends SQLiteCacheHelper {
-        private final static int DB_VERSION = 7;
+        private final static int DB_VERSION = 10;
 
         private final static int RELEASE_VERSION = DB_VERSION +
-                (FeatureFlags.LAUNCHER3_ICON_NORMALIZATION ? 1 : 0);
+                (FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? 0 : 1);
 
         private final static String TABLE_NAME = "icons";
         private final static String COLUMN_ROWID = "rowid";
@@ -831,34 +848,39 @@
         }
     }
 
-    private ContentValues newContentValues(Bitmap icon, String label, int lowResBackgroundColor) {
+    private ContentValues newContentValues(Bitmap icon, Bitmap lowResIcon, String label,
+            String packageName) {
         ContentValues values = new ContentValues();
         values.put(IconDB.COLUMN_ICON, Utilities.flattenBitmap(icon));
+        values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(lowResIcon));
 
         values.put(IconDB.COLUMN_LABEL, label);
-        values.put(IconDB.COLUMN_SYSTEM_STATE, mSystemState);
+        values.put(IconDB.COLUMN_SYSTEM_STATE, mIconProvider.getIconSystemState(packageName));
 
+        return values;
+    }
+
+    /**
+     * Generates a new low-res icon given a high-res icon.
+     */
+    private Bitmap generateLowResIcon(Bitmap icon, int lowResBackgroundColor) {
         if (lowResBackgroundColor == Color.TRANSPARENT) {
-          values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(
-          Bitmap.createScaledBitmap(icon,
-                  icon.getWidth() / LOW_RES_SCALE_FACTOR,
-                  icon.getHeight() / LOW_RES_SCALE_FACTOR, true)));
+            return Bitmap.createScaledBitmap(icon,
+                            icon.getWidth() / LOW_RES_SCALE_FACTOR,
+                            icon.getHeight() / LOW_RES_SCALE_FACTOR, true);
         } else {
+            Bitmap lowResIcon = Bitmap.createBitmap(icon.getWidth() / LOW_RES_SCALE_FACTOR,
+                    icon.getHeight() / LOW_RES_SCALE_FACTOR, Bitmap.Config.RGB_565);
             synchronized (this) {
-                if (mLowResBitmap == null) {
-                    mLowResBitmap = Bitmap.createBitmap(icon.getWidth() / LOW_RES_SCALE_FACTOR,
-                            icon.getHeight() / LOW_RES_SCALE_FACTOR, Bitmap.Config.RGB_565);
-                    mLowResCanvas = new Canvas(mLowResBitmap);
-                    mLowResPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
-                }
+                mLowResCanvas.setBitmap(lowResIcon);
                 mLowResCanvas.drawColor(lowResBackgroundColor);
                 mLowResCanvas.drawBitmap(icon, new Rect(0, 0, icon.getWidth(), icon.getHeight()),
-                        new Rect(0, 0, mLowResBitmap.getWidth(), mLowResBitmap.getHeight()),
+                        new Rect(0, 0, lowResIcon.getWidth(), lowResIcon.getHeight()),
                         mLowResPaint);
-                values.put(IconDB.COLUMN_ICON_LOW_RES, Utilities.flattenBitmap(mLowResBitmap));
+                mLowResCanvas.setBitmap(null);
             }
+            return lowResIcon;
         }
-        return values;
     }
 
     private static Bitmap loadIconNoResize(Cursor c, int iconIndex, BitmapFactory.Options options) {
diff --git a/src/com/android/launcher3/IconProvider.java b/src/com/android/launcher3/IconProvider.java
new file mode 100644
index 0000000..0a273bb
--- /dev/null
+++ b/src/com/android/launcher3/IconProvider.java
@@ -0,0 +1,49 @@
+package com.android.launcher3;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Locale;
+
+public class IconProvider {
+
+    private static final boolean DBG = false;
+    private static final String TAG = "IconProvider";
+
+    protected String mSystemState;
+
+    public IconProvider() {
+        updateSystemStateString();
+    }
+
+    public static IconProvider loadByName(String className, Context context) {
+        if (TextUtils.isEmpty(className)) return new IconProvider();
+        if (DBG) Log.d(TAG, "Loading IconProvider: " + className);
+        try {
+            Class<?> cls = Class.forName(className);
+            return (IconProvider) cls.getDeclaredConstructor(Context.class).newInstance(context);
+        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
+                | ClassCastException | NoSuchMethodException | InvocationTargetException e) {
+            Log.e(TAG, "Bad IconProvider class", e);
+            return new IconProvider();
+        }
+    }
+
+    public void updateSystemStateString() {
+        mSystemState = Locale.getDefault().toString();
+    }
+
+    public String getIconSystemState(String packageName) {
+        return mSystemState;
+    }
+
+
+    public Drawable getIcon(LauncherActivityInfoCompat info, int iconDpi) {
+        return info.getIcon(iconDpi);
+    }
+}
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index d93cdcc..398c9d2 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -16,13 +16,20 @@
 
 package com.android.launcher3;
 
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.provider.Settings;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.Toast;
 
-import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
 
-public class InfoDropTarget extends ButtonDropTarget {
+public class InfoDropTarget extends UninstallDropTarget {
+
+    private static final String TAG = "InfoDropTarget";
 
     public InfoDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -36,12 +43,24 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         // Get the hover color
-        mHoverColor = getResources().getColor(R.color.info_target_hover_tint);
+        mHoverColor = Utilities.getColorAccent(getContext());
 
         setDrawable(R.drawable.ic_info_launcher);
     }
 
-    public static void startDetailsActivityForInfo(Object info, Launcher launcher) {
+    @Override
+    void completeDrop(DragObject d) {
+        DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
+                ? (DropTargetResultCallback) d.dragSource : null;
+        startDetailsActivityForInfo(d.dragInfo, mLauncher, callback);
+    }
+
+    /**
+     * @return Whether the activity was started.
+     */
+    public static boolean startDetailsActivityForInfo(
+            ItemInfo info, Launcher launcher, DropTargetResultCallback callback) {
+        boolean result = false;
         ComponentName componentName = null;
         if (info instanceof AppInfo) {
             componentName = ((AppInfo) info).componentName;
@@ -49,30 +68,39 @@
             componentName = ((ShortcutInfo) info).intent.getComponent();
         } else if (info instanceof PendingAddItemInfo) {
             componentName = ((PendingAddItemInfo) info).componentName;
+        } else if (info instanceof LauncherAppWidgetInfo) {
+            componentName = ((LauncherAppWidgetInfo) info).providerName;
         }
-        final UserHandleCompat user;
-        if (info instanceof ItemInfo) {
-            user = ((ItemInfo) info).user;
-        } else {
-            user = UserHandleCompat.myUserHandle();
-        }
-
         if (componentName != null) {
-            launcher.startApplicationDetailsActivity(componentName, user);
+            try {
+                LauncherAppsCompat.getInstance(launcher)
+                        .showAppDetailsForProfile(componentName, info.user);
+                result = true;
+            } catch (SecurityException | ActivityNotFoundException e) {
+                Toast.makeText(launcher, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+                Log.e(TAG, "Unable to launch settings", e);
+            }
         }
+
+        if (callback != null) {
+            sendUninstallResult(launcher, result, componentName, info.user, callback);
+        }
+        return result;
     }
 
     @Override
-    protected boolean supportsDrop(DragSource source, Object info) {
-        return source.supportsAppInfoDropTarget() && supportsDrop(getContext(), info);
+    protected boolean supportsDrop(DragSource source, ItemInfo info) {
+        return source.supportsAppInfoDropTarget() && supportsDrop(info);
     }
 
-    public static boolean supportsDrop(Context context, Object info) {
-        return info instanceof AppInfo || info instanceof PendingAddItemInfo;
-    }
-
-    @Override
-    void completeDrop(DragObject d) {
-        startDetailsActivityForInfo(d.dragInfo, mLauncher);
+    public static boolean supportsDrop(ItemInfo info) {
+        // Only show the App Info drop target if developer settings are enabled.
+        ContentResolver resolver = LauncherAppState.getInstance().getContext().getContentResolver();
+        boolean developmentSettingsEnabled = Settings.Global.getInt(resolver,
+                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1;
+        return developmentSettingsEnabled
+                && (info instanceof AppInfo || info instanceof ShortcutInfo
+                || info instanceof PendingAddItemInfo || info instanceof LauncherAppWidgetInfo)
+                && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
     }
 }
diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
index 7343bf6..154641c 100644
--- a/src/com/android/launcher3/InsettableFrameLayout.java
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -5,14 +5,23 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.config.FeatureFlags;
+
 public class InsettableFrameLayout extends FrameLayout implements
     ViewGroup.OnHierarchyChangeListener, Insettable {
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     protected Rect mInsets = new Rect();
 
+    public Rect getInsets() {
+        return mInsets;
+    }
+
     public InsettableFrameLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
         setOnHierarchyChangeListener(this);
@@ -34,6 +43,10 @@
 
     @Override
     public void setInsets(Rect insets) {
+        // If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by
+        // modifying child layout params.
+        if (insets.equals(mInsets)) return;
+
         final int n = getChildCount();
         for (int i = 0; i < n; i++) {
             final View child = getChildAt(i);
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 921e90c..d8e58d8 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Thunk;
 
 import org.json.JSONException;
@@ -146,6 +147,15 @@
         }
         PendingInstallShortcutInfo info = createPendingInfo(context, data);
         if (info != null) {
+            if (!info.isLauncherActivity()) {
+                // Since its a custom shortcut, verify that it is safe to launch.
+                if (!PackageManagerHelper.hasPermissionForActivity(
+                        context, info.launchIntent, null)) {
+                    // Target cannot be launched, or requires some special permission to launch
+                    Log.e(TAG, "Ignoring malicious intent " + info.launchIntent.toUri(0));
+                    return;
+                }
+            }
             queuePendingShortcutInfo(info, context);
         }
     }
@@ -348,7 +358,7 @@
 
         public ShortcutInfo getShortcutInfo() {
             if (activityInfo != null) {
-                return ShortcutInfo.fromActivityInfo(activityInfo, mContext);
+                return new ShortcutInfo(activityInfo, mContext);
             } else {
                 return LauncherAppState.getInstance().getModel().infoFromShortcutIntent(mContext, data);
             }
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index d601322..38545e2 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -18,13 +18,22 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
 import android.graphics.Point;
 import android.util.DisplayMetrics;
+import android.util.Xml;
 import android.view.Display;
 import android.view.WindowManager;
 
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.util.Thunk;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -58,6 +67,7 @@
     /**
      * The minimum number of predicted apps in all apps.
      */
+    @Deprecated
     int minAllAppsPredictionColumns;
 
     /**
@@ -77,12 +87,11 @@
     float hotseatIconSize;
     int defaultLayoutId;
 
-    // Derived invariant properties
-    public int hotseatAllAppsRank;
-
     DeviceProfile landscapeProfile;
     DeviceProfile portraitProfile;
 
+    public Point defaultWallpaperSize;
+
     public InvariantDeviceProfile() {
     }
 
@@ -95,11 +104,6 @@
 
     InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc, int maapc,
             float is, float its, int hs, float his, int dlId) {
-        // Ensure that we have an odd number of hotseat items (since we need to place all apps)
-        if (hs % 2 == 0) {
-            throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces");
-        }
-
         name = n;
         minWidthDps = w;
         minHeightDps = h;
@@ -130,8 +134,8 @@
         minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y), dm);
         minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm);
 
-        ArrayList<InvariantDeviceProfile> closestProfiles =
-                findClosestDeviceProfiles(minWidthDps, minHeightDps, getPredefinedDeviceProfiles());
+        ArrayList<InvariantDeviceProfile> closestProfiles = findClosestDeviceProfiles(
+                minWidthDps, minHeightDps, getPredefinedDeviceProfiles(context));
         InvariantDeviceProfile interpolatedDeviceProfileOut =
                 invDistWeightedInterpolate(minWidthDps,  minHeightDps, closestProfiles);
 
@@ -139,7 +143,6 @@
         numRows = closestProfile.numRows;
         numColumns = closestProfile.numColumns;
         numHotseatIcons = closestProfile.numHotseatIcons;
-        hotseatAllAppsRank = (int) (numHotseatIcons / 2);
         defaultLayoutId = closestProfile.defaultLayoutId;
         numFolderRows = closestProfile.numFolderRows;
         numFolderColumns = closestProfile.numFolderColumns;
@@ -166,38 +169,53 @@
                 largeSide, smallSide, true /* isLandscape */);
         portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize,
                 smallSide, largeSide, false /* isLandscape */);
+
+        // We need to ensure that there is enough extra space in the wallpaper
+        // for the intended parallax effects
+        if (context.getResources().getConfiguration().smallestScreenWidthDp >= 720) {
+            defaultWallpaperSize = new Point(
+                    (int) (largeSide * wallpaperTravelToScreenWidthRatio(largeSide, smallSide)),
+                    largeSide);
+        } else {
+            defaultWallpaperSize = new Point(Math.max(smallSide * 2, largeSide), largeSide);
+        }
     }
 
-    ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles() {
-        ArrayList<InvariantDeviceProfile> predefinedDeviceProfiles = new ArrayList<>();
-        // width, height, #rows, #columns, #folder rows, #folder columns,
-        // iconSize, iconTextSize, #hotseat, #hotseatIconSize, defaultLayoutId.
-        predefinedDeviceProfiles.add(new InvariantDeviceProfile("Super Short Stubby",
-                255, 300,     2, 3, 2, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_3x3));
-        predefinedDeviceProfiles.add(new InvariantDeviceProfile("Shorter Stubby",
-                255, 400,     3, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_3x3));
-        predefinedDeviceProfiles.add(new InvariantDeviceProfile("Short Stubby",
-                275, 420,     3, 4, 3, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
-        predefinedDeviceProfiles.add(new InvariantDeviceProfile("Stubby",
-                255, 450,     3, 4, 3, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
-        predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus S",
-                296, 491.33f, 4, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
-        predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 4",
-                359, 567,     4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
-        predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5",
-                335, 567,     4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
-        predefinedDeviceProfiles.add(new InvariantDeviceProfile("Large Phone",
-                406, 694,     5, 5, 4, 4, 4, 64, 14.4f,  5, 56, R.xml.default_workspace_5x5));
-        // The tablet profile is odd in that the landscape orientation
-        // also includes the nav bar on the side
-        predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 7",
-                575, 904,     5, 6, 4, 5, 4, 72, 14.4f,  7, 60, R.xml.default_workspace_5x6));
-        // Larger tablet profiles always have system bars on the top & bottom
-        predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 10",
-                727, 1207,    5, 6, 4, 5, 4, 76, 14.4f,  7, 76, R.xml.default_workspace_5x6));
-        predefinedDeviceProfiles.add(new InvariantDeviceProfile("20-inch Tablet",
-                1527, 2527,   7, 7, 6, 6, 4, 100, 20,  7, 72, R.xml.default_workspace_5x6));
-        return predefinedDeviceProfiles;
+    ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles(Context context) {
+        ArrayList<InvariantDeviceProfile> profiles = new ArrayList<>();
+        try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
+            final int depth = parser.getDepth();
+            int type;
+
+            while (((type = parser.next()) != XmlPullParser.END_TAG ||
+                    parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+                if ((type == XmlPullParser.START_TAG) && "profile".equals(parser.getName())) {
+                    TypedArray a = context.obtainStyledAttributes(
+                            Xml.asAttributeSet(parser), R.styleable.InvariantDeviceProfile);
+                    int numRows = a.getInt(R.styleable.InvariantDeviceProfile_numRows, 0);
+                    int numColumns = a.getInt(R.styleable.InvariantDeviceProfile_numColumns, 0);
+                    float iconSize = a.getFloat(R.styleable.InvariantDeviceProfile_iconSize, 0);
+                    profiles.add(new InvariantDeviceProfile(
+                            a.getString(R.styleable.InvariantDeviceProfile_name),
+                            a.getFloat(R.styleable.InvariantDeviceProfile_minWidthDps, 0),
+                            a.getFloat(R.styleable.InvariantDeviceProfile_minHeightDps, 0),
+                            numRows,
+                            numColumns,
+                            a.getInt(R.styleable.InvariantDeviceProfile_numFolderRows, numRows),
+                            a.getInt(R.styleable.InvariantDeviceProfile_numFolderColumns, numColumns),
+                            a.getInt(R.styleable.InvariantDeviceProfile_minAllAppsPredictionColumns, numColumns),
+                            iconSize,
+                            a.getFloat(R.styleable.InvariantDeviceProfile_iconTextSize, 0),
+                            a.getInt(R.styleable.InvariantDeviceProfile_numHotseatIcons, numColumns),
+                            a.getFloat(R.styleable.InvariantDeviceProfile_hotseatIconSize, iconSize),
+                            a.getResourceId(R.styleable.InvariantDeviceProfile_defaultLayoutId, 0)));
+                    a.recycle();
+                }
+            }
+        } catch (IOException|XmlPullParserException e) {
+            throw new RuntimeException(e);
+        }
+        return profiles;
     }
 
     private int getLauncherIconDensity(int requiredSize) {
@@ -292,6 +310,17 @@
         return this;
     }
 
+    public int getAllAppsButtonRank() {
+        if (ProviderConfig.IS_DOGFOOD_BUILD && FeatureFlags.NO_ALL_APPS_ICON) {
+            throw new IllegalAccessError("Accessing all apps rank when all-apps is disabled");
+        }
+        return numHotseatIcons / 2;
+    }
+
+    public boolean isAllAppsButtonRank(int rank) {
+        return rank == getAllAppsButtonRank();
+    }
+
     private float weight(float x0, float y0, float x1, float y1, float pow) {
         float d = dist(x0, y0, x1, y1);
         if (Float.compare(d, 0f) == 0) {
@@ -299,4 +328,34 @@
         }
         return (float) (WEIGHT_EFFICIENT / Math.pow(d, pow));
     }
+
+    /**
+     * As a ratio of screen height, the total distance we want the parallax effect to span
+     * horizontally
+     */
+    private static float wallpaperTravelToScreenWidthRatio(int width, int height) {
+        float aspectRatio = width / (float) height;
+
+        // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width
+        // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width
+        // We will use these two data points to extrapolate how much the wallpaper parallax effect
+        // to span (ie travel) at any aspect ratio:
+
+        final float ASPECT_RATIO_LANDSCAPE = 16/10f;
+        final float ASPECT_RATIO_PORTRAIT = 10/16f;
+        final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f;
+        final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f;
+
+        // To find out the desired width at different aspect ratios, we use the following two
+        // formulas, where the coefficient on x is the aspect ratio (width/height):
+        //   (16/10)x + y = 1.5
+        //   (10/16)x + y = 1.2
+        // We solve for x and y and end up with a final formula:
+        final float x =
+                (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
+                        (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT);
+        final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT;
+        return x * aspectRatio + y;
+    }
+
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index c7a6807..c0c22a3 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
@@ -24,8 +25,6 @@
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 
-import java.util.Arrays;
-
 /**
  * Represents an item in the launcher.
  */
@@ -34,15 +33,15 @@
     /**
      * Intent extra to store the profile. Format: UserHandle
      */
-    static final String EXTRA_PROFILE = "profile";
-    
+    public static final String EXTRA_PROFILE = "profile";
+
     public static final int NO_ID = -1;
-    
+
     /**
      * The id in the settings database for this item
      */
     public long id = NO_ID;
-    
+
     /**
      * One of {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
      * {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
@@ -50,20 +49,20 @@
      * {@link LauncherSettings.Favorites#ITEM_TYPE_APPWIDGET}.
      */
     public int itemType;
-    
+
     /**
-     * The id of the container that holds this item. For the desktop, this will be 
+     * The id of the container that holds this item. For the desktop, this will be
      * {@link LauncherSettings.Favorites#CONTAINER_DESKTOP}. For the all applications folder it
      * will be {@link #NO_ID} (since it is not stored in the settings DB). For user folders
      * it will be the id of the folder.
      */
     public long container = NO_ID;
-    
+
     /**
      * Iindicates the screen in which the shortcut appears.
      */
     public long screenId = -1;
-    
+
     /**
      * Indicates the X position of the associated cell.
      */
@@ -100,11 +99,6 @@
     public int rank = 0;
 
     /**
-     * Indicates that this item needs to be updated in the db
-     */
-    public boolean requiresDbUpdate = false;
-
-    /**
      * Title of the item
      */
     public CharSequence title;
@@ -114,11 +108,6 @@
      */
     public CharSequence contentDescription;
 
-    /**
-     * The position of the item in a drag-and-drop operation.
-     */
-    public int[] dropPos = null;
-
     public UserHandleCompat user;
 
     public ItemInfo() {
@@ -146,18 +135,15 @@
     }
 
     public Intent getIntent() {
-        throw new RuntimeException("Unexpected Intent");
+        return null;
     }
 
-    /**
-     * Write the fields of this item to the DB
-     * 
-     * @param context A context object to use for getting UserManagerCompat
-     * @param values
-     */
+    public ComponentName getTargetComponent() {
+        return getIntent() == null ? null : getIntent().getComponent();
+    }
 
-    void onAddToDatabase(Context context, ContentValues values) {
-        values.put(LauncherSettings.BaseLauncherColumns.ITEM_TYPE, itemType);
+    public void writeToValues(ContentValues values) {
+        values.put(LauncherSettings.Favorites.ITEM_TYPE, itemType);
         values.put(LauncherSettings.Favorites.CONTAINER, container);
         values.put(LauncherSettings.Favorites.SCREEN, screenId);
         values.put(LauncherSettings.Favorites.CELLX, cellX);
@@ -165,6 +151,27 @@
         values.put(LauncherSettings.Favorites.SPANX, spanX);
         values.put(LauncherSettings.Favorites.SPANY, spanY);
         values.put(LauncherSettings.Favorites.RANK, rank);
+    }
+
+    public void readFromValues(ContentValues values) {
+        itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE);
+        container = values.getAsLong(LauncherSettings.Favorites.CONTAINER);
+        screenId = values.getAsLong(LauncherSettings.Favorites.SCREEN);
+        cellX = values.getAsInteger(LauncherSettings.Favorites.CELLX);
+        cellY = values.getAsInteger(LauncherSettings.Favorites.CELLY);
+        spanX = values.getAsInteger(LauncherSettings.Favorites.SPANX);
+        spanY = values.getAsInteger(LauncherSettings.Favorites.SPANY);
+        rank = values.getAsInteger(LauncherSettings.Favorites.RANK);
+    }
+
+    /**
+     * Write the fields of this item to the DB
+     *
+     * @param context A context object to use for getting UserManagerCompat
+     * @param values
+     */
+    void onAddToDatabase(Context context, ContentValues values) {
+        writeToValues(values);
         long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
         values.put(LauncherSettings.Favorites.PROFILE_ID, serialNumber);
 
@@ -181,21 +188,25 @@
         }
     }
 
-    /**
-     * It is very important that sub-classes implement this if they contain any references
-     * to the activity (anything in the view hierarchy etc.). If not, leaks can result since
-     * ItemInfo objects persist across rotation and can hence leak by holding stale references
-     * to the old view hierarchy / activity.
-     */
-    void unbind() {
+    @Override
+    public final String toString() {
+        return getClass().getSimpleName() + "(" + dumpProperties() + ")";
     }
 
-    @Override
-    public String toString() {
-        return "Item(id=" + this.id + " type=" + this.itemType + " container=" + this.container
-            + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
-            + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
-            + " user=" + user + ")";
+    protected String dumpProperties() {
+        return "id=" + id
+                + " type=" + itemType
+                + " container=" + container
+                + " screen=" + screenId
+                + " cellX=" + cellX
+                + " cellY=" + cellY
+                + " spanX=" + spanX
+                + " spanY=" + spanY
+                + " minSpanX=" + minSpanX
+                + " minSpanY=" + minSpanY
+                + " rank=" + rank
+                + " user=" + user
+                + " title=" + title;
     }
 
     /**
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 91b7477..4672e08 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -21,12 +21,10 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.app.Activity;
-import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.AlertDialog;
 import android.app.SearchManager;
@@ -38,34 +36,30 @@
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Point;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.Message;
 import android.os.StrictMode;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
@@ -75,7 +69,6 @@
 import android.view.Display;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
-import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -83,10 +76,9 @@
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
-import android.view.ViewStub;
 import android.view.ViewTreeObserver;
-import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 import android.view.animation.OvershootInterpolator;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Advanceable;
@@ -95,33 +87,52 @@
 import android.widget.Toast;
 
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.PagedView.PageSwitchListener;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.allapps.DefaultAppSearchController;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dynamicui.ExtractedColors;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.keyboard.ViewGroupFocusHelper;
+import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.pageindicators.PageIndicator;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer;
+import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.ActivityResultInfo;
 import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.LongArrayMap;
+import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.MultiHashMap;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.PendingRequestArgs;
 import com.android.launcher3.util.TestingUtils;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.ViewOnDrawExecutor;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.WidgetHostViewLoader;
 import com.android.launcher3.widget.WidgetsContainerView;
 
-import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.IOException;
 import java.io.PrintWriter;
-import java.text.DateFormat;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -130,18 +141,15 @@
  * Default launcher application.
  */
 public class Launcher extends Activity
-        implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
-                   View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
-    static final String TAG = "Launcher";
+        implements LauncherExterns, View.OnClickListener, OnLongClickListener,
+                   LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,
+                   AccessibilityManager.AccessibilityStateChangeListener {
+    public static final String TAG = "Launcher";
     static final boolean LOGD = false;
 
-    static final boolean PROFILE_STARTUP = false;
     static final boolean DEBUG_WIDGETS = false;
     static final boolean DEBUG_STRICT_MODE = false;
     static final boolean DEBUG_RESUME_TIME = false;
-    static final boolean DEBUG_DUMP_LOG = false;
-
-    static final boolean ENABLE_DEBUG_INTENTS = false; // allow DebugIntents to run
 
     private static final int REQUEST_CREATE_SHORTCUT = 1;
     private static final int REQUEST_CREATE_APPWIDGET = 5;
@@ -149,14 +157,11 @@
     private static final int REQUEST_PICK_WALLPAPER = 10;
 
     private static final int REQUEST_BIND_APPWIDGET = 11;
+    private static final int REQUEST_BIND_PENDING_APPWIDGET = 14;
     private static final int REQUEST_RECONFIGURE_APPWIDGET = 12;
 
     private static final int REQUEST_PERMISSION_CALL_PHONE = 13;
 
-    private static final int WORKSPACE_BACKGROUND_GRADIENT = 0;
-    private static final int WORKSPACE_BACKGROUND_TRANSPARENT = 1;
-    private static final int WORKSPACE_BACKGROUND_BLACK = 2;
-
     private static final float BOUNCE_ANIMATION_TENSION = 1.3f;
 
     /**
@@ -165,51 +170,31 @@
      */
     protected static final int REQUEST_LAST = 100;
 
-    static final int SCREEN_COUNT = 5;
-
     // To turn on these properties, type
-    // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
+    // adb shell setprop logTap.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
     static final String DUMP_STATE_PROPERTY = "launcher_dump_state";
 
     // The Intent extra that defines whether to ignore the launch animation
     static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
             "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
 
+    public static final String ACTION_APPWIDGET_HOST_RESET =
+            "com.android.launcher3.intent.ACTION_APPWIDGET_HOST_RESET";
+
     // Type: int
     private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen";
     // Type: int
     private static final String RUNTIME_STATE = "launcher.state";
-    // Type: int
-    private static final String RUNTIME_STATE_PENDING_ADD_CONTAINER = "launcher.add_container";
-    // Type: int
-    private static final String RUNTIME_STATE_PENDING_ADD_SCREEN = "launcher.add_screen";
-    // Type: int
-    private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cell_x";
-    // Type: int
-    private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cell_y";
-    // Type: int
-    private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_span_x";
-    // Type: int
-    private static final String RUNTIME_STATE_PENDING_ADD_SPAN_Y = "launcher.add_span_y";
-    // Type: parcelable
-    private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info";
-    // Type: parcelable
-    private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_ID = "launcher.add_widget_id";
+    // Type: PendingRequestArgs
+    private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args";
+    // Type: ActivityResultInfo
+    private static final String RUNTIME_STATE_PENDING_ACTIVITY_RESULT = "launcher.activity_result";
 
-    static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed";
-    static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
-
-    static final String FIRST_LOAD_COMPLETE = "launcher.first_load_complete";
-    static final String ACTION_FIRST_LOAD_COMPLETE =
-            "com.android.launcher3.action.FIRST_LOAD_COMPLETE";
-
-    private static final String QSB_WIDGET_ID = "qsb_widget_id";
-    private static final String QSB_WIDGET_PROVIDER = "qsb_widget_provider";
-
-    public static final String USER_HAS_MIGRATED = "launcher.user_migrated_from_old_data";
+    static final String APPS_VIEW_SHOWN = "launcher.apps_view_shown";
 
     /** The different states that Launcher can be in. */
-    enum State { NONE, WORKSPACE, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED }
+    enum State { NONE, WORKSPACE, WORKSPACE_SPRING_LOADED, APPS, APPS_SPRING_LOADED,
+        WIDGETS, WIDGETS_SPRING_LOADED }
 
     @Thunk State mState = State.WORKSPACE;
     @Thunk LauncherStateTransitionAnimation mStateTransitionAnimation;
@@ -217,7 +202,7 @@
     private boolean mIsSafeModeEnabled;
 
     static final int APPWIDGET_HOST_ID = 1024;
-    public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
+    public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 500;
     private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
     private static final int ACTIVITY_START_DELAY = 1000;
 
@@ -226,26 +211,29 @@
     private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
     @Thunk static int NEW_APPS_ANIMATION_DELAY = 500;
 
-    private final BroadcastReceiver mCloseSystemDialogsReceiver
-            = new CloseSystemDialogsIntentReceiver();
+    private final BroadcastReceiver mUiBroadcastReceiver = new BroadcastReceiver() {
 
-    private LayoutInflater mInflater;
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (ACTION_APPWIDGET_HOST_RESET.equals(intent.getAction())) {
+                if (mAppWidgetHost != null) {
+                    mAppWidgetHost.startListening();
+                }
+            }
+        }
+    };
 
     @Thunk Workspace mWorkspace;
     private View mLauncherView;
-    private View mPageIndicators;
     @Thunk DragLayer mDragLayer;
     private DragController mDragController;
+    private View mQsbContainer;
 
     public View mWeightWatcher;
 
     private AppWidgetManagerCompat mAppWidgetManager;
     private LauncherAppWidgetHost mAppWidgetHost;
 
-    @Thunk ItemInfo mPendingAddInfo = new ItemInfo();
-    private LauncherAppWidgetProviderInfo mPendingAddWidgetInfo;
-    private int mPendingAddWidgetId = -1;
-
     private int[] mTmpAddItemCellCoordinates = new int[2];
 
     @Thunk Hotseat mHotseat;
@@ -254,18 +242,16 @@
     private View mAllAppsButton;
     private View mWidgetsButton;
 
-    private SearchDropTargetBar mSearchDropTargetBar;
+    private DropTargetBar mDropTargetBar;
 
     // Main container view for the all apps screen.
     @Thunk AllAppsContainerView mAppsView;
+    AllAppsTransitionController mAllAppsController;
 
     // Main container view and the model for the widget tray screen.
     @Thunk WidgetsContainerView mWidgetsView;
     @Thunk WidgetsModel mWidgetsModel;
 
-    private boolean mAutoAdvanceRunning = false;
-    private AppWidgetHostView mQsb;
-
     private Bundle mSavedState;
     // We set the state in both onCreate and then onNewIntent in some cases, which causes both
     // scroll issues (because the workspace may not have been measured yet) and extra work.
@@ -277,52 +263,42 @@
     @Thunk boolean mWorkspaceLoading = true;
 
     private boolean mPaused = true;
-    private boolean mRestoring;
-    private boolean mWaitingForResult;
     private boolean mOnResumeNeedsLoad;
 
     private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>();
     private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>();
-
-    private Bundle mSavedInstanceState;
+    private ViewOnDrawExecutor mPendingExecutor;
 
     private LauncherModel mModel;
     private IconCache mIconCache;
+    private ExtractedColors mExtractedColors;
+    private LauncherAccessibilityDelegate mAccessibilityDelegate;
+    private boolean mIsResumeFromActionScreenOff;
     @Thunk boolean mUserPresent = true;
-    private boolean mVisible = false;
-    private boolean mHasFocus = false;
-    private boolean mAttached = false;
+    private boolean mVisible;
+    private boolean mHasFocus;
+    private boolean mAttached;
 
-    private LauncherClings mClings;
-
-    private static LongArrayMap<FolderInfo> sFolders = new LongArrayMap<>();
+    /** Maps launcher activity components to their list of shortcut ids. */
+    private MultiHashMap<ComponentKey, String> mDeepShortcutMap = new MultiHashMap<>();
 
     private View.OnTouchListener mHapticFeedbackTouchListener;
 
     // Related to the auto-advancing of widgets
     private final int ADVANCE_MSG = 1;
-    private final int mAdvanceInterval = 20000;
-    private final int mAdvanceStagger = 250;
+    private static final int ADVANCE_INTERVAL = 20000;
+    private static final int ADVANCE_STAGGER = 250;
+
+    private boolean mAutoAdvanceRunning = false;
     private long mAutoAdvanceSentTime;
     private long mAutoAdvanceTimeLeft = -1;
-    @Thunk HashMap<View, AppWidgetProviderInfo> mWidgetsToAdvance =
-        new HashMap<View, AppWidgetProviderInfo>();
+    @Thunk HashMap<View, AppWidgetProviderInfo> mWidgetsToAdvance = new HashMap<>();
 
     // Determines how long to wait after a rotation before restoring the screen orientation to
     // match the sensor state.
-    private final int mRestoreScreenOrientationDelay = 500;
-
-    @Thunk Drawable mWorkspaceBackgroundDrawable;
+    private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
 
     private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
-    private static final boolean DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE = false;
-
-    static final ArrayList<String> sDumpLogs = new ArrayList<String>();
-    static Date sDateStamp = new Date();
-    static DateFormat sDateFormat =
-            DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
-    static long sRunStart = System.currentTimeMillis();
-    static final String CORRUPTION_EMAIL_SENT_KEY = "corruptionEmailSent";
 
     // We only want to get the SharedPreferences once since it does an FS stat each time we get
     // it from the context.
@@ -353,6 +329,11 @@
         }
     }
 
+    // Exiting spring loaded mode happens with a delay. This runnable object triggers the
+    // state transition. If another state transition happened during this delay,
+    // simply unregister this runnable.
+    private Runnable mExitSpringLoadedModeRunnable;
+
     @Thunk Runnable mBuildLayersRunnable = new Runnable() {
         public void run() {
             if (mWorkspace != null) {
@@ -361,20 +342,17 @@
         }
     };
 
-    private static PendingAddArguments sPendingAddItem;
+    // Activity result which needs to be processed after workspace has loaded.
+    private ActivityResultInfo mPendingActivityResult;
+    /**
+     * Holds extra information required to handle a result from an external call, like
+     * {@link #startActivityForResult(Intent, int)} or {@link #requestPermissions(String[], int)}
+     */
+    private PendingRequestArgs mPendingRequestArgs;
 
-    @Thunk static class PendingAddArguments {
-        int requestCode;
-        Intent intent;
-        long container;
-        long screenId;
-        int cellX;
-        int cellY;
-        int appWidgetId;
-    }
+    private UserEventDispatcher mUserEventDispatcher;
 
-    private Stats mStats;
-    FocusIndicatorView mFocusHandler;
+    public ViewGroupFocusHelper mFocusHandler;
     private boolean mRotationEnabled = false;
 
     @Thunk void setOrientation() {
@@ -386,11 +364,7 @@
         }
     }
 
-    private Runnable mUpdateOrientationRunnable = new Runnable() {
-        public void run() {
-            setOrientation();
-        }
-    };
+    private RotationPrefChangeHandler mRotationPrefChangeHandler;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -408,6 +382,9 @@
                     .penaltyDeath()
                     .build());
         }
+        if (LauncherAppState.PROFILE_STARTUP) {
+            Trace.beginSection("Launcher-onCreate");
+        }
 
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.preOnCreate();
@@ -427,12 +404,11 @@
         mIsSafeModeEnabled = getPackageManager().isSafeMode();
         mModel = app.setLauncher(this);
         mIconCache = app.getIconCache();
+        mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
 
         mDragController = new DragController(this);
-        mInflater = getLayoutInflater();
-        mStateTransitionAnimation = new LauncherStateTransitionAnimation(this);
-
-        mStats = new Stats(this);
+        mAllAppsController = new AllAppsTransitionController(this);
+        mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, mAllAppsController);
 
         mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
 
@@ -444,51 +420,49 @@
         // LauncherModel load.
         mPaused = false;
 
-        if (PROFILE_STARTUP) {
-            android.os.Debug.startMethodTracing(
-                    Environment.getExternalStorageDirectory() + "/launcher");
-        }
-
         setContentView(R.layout.launcher);
 
-        app.getInvariantDeviceProfile().landscapeProfile.setSearchBarHeight(getSearchBarHeight());
-        app.getInvariantDeviceProfile().portraitProfile.setSearchBarHeight(getSearchBarHeight());
         setupViews();
-        mDeviceProfile.layout(this);
+        mDeviceProfile.layout(this, false /* notifyListeners */);
+        mExtractedColors = new ExtractedColors();
+        loadExtractedColorsAndColorItems();
+
+        ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
+                .addAccessibilityStateChangeListener(this);
 
         lockAllApps();
 
         mSavedState = savedInstanceState;
         restoreState(mSavedState);
 
-        if (PROFILE_STARTUP) {
-            android.os.Debug.stopMethodTracing();
+        if (LauncherAppState.PROFILE_STARTUP) {
+            Trace.endSection();
         }
 
-        if (!mRestoring) {
-            if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
-                // If the user leaves launcher, then we should just load items asynchronously when
-                // they return.
-                mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
-            } else {
-                // We only load the page synchronously if the user rotates (or triggers a
-                // configuration change) while launcher is in the foreground
-                mModel.startLoader(mWorkspace.getRestorePage());
-            }
+        // We only load the page synchronously if the user rotates (or triggers a
+        // configuration change) while launcher is in the foreground
+        if (!mModel.startLoader(mWorkspace.getRestorePage())) {
+            // If we are not binding synchronously, show a fade in animation when
+            // the first page bind completes.
+            mDragLayer.setAlpha(0);
+        } else {
+            setWorkspaceLoading(true);
         }
 
         // For handling default keys
         mDefaultKeySsb = new SpannableStringBuilder();
         Selection.setSelection(mDefaultKeySsb, 0);
 
-        IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
-        registerReceiver(mCloseSystemDialogsReceiver, filter);
+        IntentFilter filter = new IntentFilter(ACTION_APPWIDGET_HOST_RESET);
+        registerReceiver(mUiBroadcastReceiver, filter);
 
         mRotationEnabled = getResources().getBoolean(R.bool.allow_rotation);
         // In case we are on a device with locked rotation, we should look at preferences to check
         // if the user has specifically allowed rotation.
         if (!mRotationEnabled) {
             mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext());
+            mRotationPrefChangeHandler = new RotationPrefChangeHandler();
+            mSharedPrefs.registerOnSharedPreferenceChangeListener(mRotationPrefChangeHandler);
         }
 
         // On large interfaces, or on devices that a user has specifically enabled screen rotation,
@@ -498,22 +472,42 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onCreate(savedInstanceState);
         }
-
-        if (shouldShowIntroScreen()) {
-            showIntroScreen();
-        } else {
-            showFirstRunActivity();
-            showFirstRunClings();
-        }
     }
 
     @Override
-    public void onSettingsChanged(String settings, boolean value) {
-        if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(settings)) {
-            mRotationEnabled = value;
-            if (!waitUntilResume(mUpdateOrientationRunnable, true)) {
-                mUpdateOrientationRunnable.run();
-            }
+    public void onExtractedColorsChanged() {
+        loadExtractedColorsAndColorItems();
+    }
+
+    private void loadExtractedColorsAndColorItems() {
+        // TODO: do this in pre-N as well, once the extraction part is complete.
+        if (Utilities.isNycOrAbove()) {
+            mExtractedColors.load(this);
+            mHotseat.updateColor(mExtractedColors, !mPaused);
+            mWorkspace.getPageIndicator().updateColor(mExtractedColors);
+            // It's possible that All Apps is visible when this is run,
+            // so always use light status bar in that case.
+            activateLightStatusBar(isAllAppsVisible());
+        }
+    }
+
+    /**
+     * Sets the status bar to be light or not. Light status bar means dark icons.
+     * @param activate if true, make sure the status bar is light, otherwise base on wallpaper.
+     */
+    public void activateLightStatusBar(boolean activate) {
+        boolean lightStatusBar = activate || (FeatureFlags.LIGHT_STATUS_BAR
+                && mExtractedColors.getColor(ExtractedColors.STATUS_BAR_INDEX,
+                ExtractedColors.DEFAULT_DARK) == ExtractedColors.DEFAULT_LIGHT);
+        int oldSystemUiFlags = getWindow().getDecorView().getSystemUiVisibility();
+        int newSystemUiFlags = oldSystemUiFlags;
+        if (lightStatusBar) {
+            newSystemUiFlags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+        } else {
+            newSystemUiFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+        }
+        if (newSystemUiFlags != oldSystemUiFlags) {
+            getWindow().getDecorView().setSystemUiVisibility(newSystemUiFlags);
         }
     }
 
@@ -526,6 +520,11 @@
         }
     }
 
+    public void onInsetsChanged(Rect insets) {
+        mDeviceProfile.updateInsets(insets);
+        mDeviceProfile.layout(this, true /* notifyListeners */);
+    }
+
     /**
      * Call this after onCreate to set or clear overlay.
      */
@@ -542,7 +541,7 @@
             private boolean mWorkspaceImportanceStored = false;
             private boolean mHotseatImportanceStored = false;
             private int mWorkspaceImportanceForAccessibility =
-                View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
+                    View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
             private int mHotseatImportanceForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
 
             @Override
@@ -551,7 +550,7 @@
                     return;
                 }
                 // The underlying workspace and hotseat are temporarily suppressed by the search
-                // overlay. So they sholudn't be accessible.
+                // overlay. So they shouldn't be accessible.
                 if (mWorkspace != null) {
                     mWorkspaceImportanceForAccessibility =
                             mWorkspace.getImportantForAccessibility();
@@ -589,14 +588,6 @@
         }
     }
 
-    /**
-     * Updates the bounds of all the overlays to match the new fixed bounds.
-     */
-    public void updateOverlayBounds(Rect newBounds) {
-        mAppsView.setSearchBarBounds(newBounds);
-        mWidgetsView.setSearchBarBounds(newBounds);
-    }
-
     /** To be overridden by subclasses to hint to Launcher that we have custom content */
     protected boolean hasCustomContentToLeft() {
         if (mLauncherCallbacks != null) {
@@ -635,12 +626,21 @@
         }
     }
 
-    public Stats getStats() {
-        return mStats;
-    }
+    public UserEventDispatcher getUserEventDispatcher() {
+        if (mLauncherCallbacks != null) {
+            UserEventDispatcher dispatcher = mLauncherCallbacks.getUserEventDispatcher();
+            if (dispatcher != null) {
+                return dispatcher;
+            }
+        }
 
-    public LayoutInflater getInflater() {
-        return mInflater;
+        // Logger object is a singleton and does not have to be coupled with the foreground
+        // activity. Since most user event logging is done on the UI, the object is retrieved
+        // from the callback for convenience.
+        if (mUserEventDispatcher == null) {
+            mUserEventDispatcher = new UserEventDispatcher();
+        }
+        return mUserEventDispatcher;
     }
 
     public boolean isDraggingEnabled() {
@@ -661,39 +661,61 @@
      * Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
      * a configuration step, this allows the proper animations to run after other transitions.
      */
-    private long completeAdd(PendingAddArguments args) {
-        long screenId = args.screenId;
-        if (args.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+    private long completeAdd(
+            int requestCode, Intent intent, int appWidgetId, PendingRequestArgs info) {
+        long screenId = info.screenId;
+        if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
             // When the screen id represents an actual screen (as opposed to a rank) we make sure
             // that the drop page actually exists.
-            screenId = ensurePendingDropLayoutExists(args.screenId);
+            screenId = ensurePendingDropLayoutExists(info.screenId);
         }
 
-        switch (args.requestCode) {
+        switch (requestCode) {
             case REQUEST_CREATE_SHORTCUT:
-                completeAddShortcut(args.intent, args.container, screenId, args.cellX,
-                        args.cellY);
+                completeAddShortcut(intent, info.container, screenId, info.cellX, info.cellY, info);
                 break;
             case REQUEST_CREATE_APPWIDGET:
-                completeAddAppWidget(args.appWidgetId, args.container, screenId, null, null);
+                completeAddAppWidget(appWidgetId, info, null, null);
                 break;
             case REQUEST_RECONFIGURE_APPWIDGET:
-                completeRestoreAppWidget(args.appWidgetId);
+                completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED);
                 break;
+            case REQUEST_BIND_PENDING_APPWIDGET: {
+                int widgetId = appWidgetId;
+                LauncherAppWidgetInfo widgetInfo =
+                        completeRestoreAppWidget(widgetId, LauncherAppWidgetInfo.FLAG_UI_NOT_READY);
+                if (widgetInfo != null) {
+                    // Since the view was just bound, also launch the configure activity if needed
+                    LauncherAppWidgetProviderInfo provider = mAppWidgetManager
+                            .getLauncherAppWidgetInfo(widgetId);
+                    if (provider != null && provider.configure != null) {
+                        startRestoredWidgetReconfigActivity(provider, widgetInfo);
+                    }
+                }
+                break;
+            }
         }
-        // Before adding this resetAddInfo(), after a shortcut was added to a workspace screen,
-        // if you turned the screen off and then back while in All Apps, Launcher would not
-        // return to the workspace. Clearing mAddInfo.container here fixes this issue
-        resetAddInfo();
+
         return screenId;
     }
 
     private void handleActivityResult(
             final int requestCode, final int resultCode, final Intent data) {
+        if (isWorkspaceLoading()) {
+            // process the result once the workspace has loaded.
+            mPendingActivityResult = new ActivityResultInfo(requestCode, resultCode, data);
+            return;
+        }
+        mPendingActivityResult = null;
+
         // Reset the startActivity waiting flag
-        setWaitingForResult(false);
-        final int pendingAddWidgetId = mPendingAddWidgetId;
-        mPendingAddWidgetId = -1;
+        final PendingRequestArgs requestArgs = mPendingRequestArgs;
+        setWaitingForResult(null);
+        if (requestArgs == null) {
+            return;
+        }
+
+        final int pendingAddWidgetId = requestArgs.getWidgetId();
 
         Runnable exitSpringLoaded = new Runnable() {
             @Override
@@ -708,22 +730,20 @@
             final int appWidgetId = data != null ?
                     data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
             if (resultCode == RESULT_CANCELED) {
-                completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
+                completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId, requestArgs);
                 mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
                         ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
             } else if (resultCode == RESULT_OK) {
-                addAppWidgetImpl(appWidgetId, mPendingAddInfo, null,
-                        mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
-
-                // When the user has granted permission to bind widgets, we should check to see if
-                // we can inflate the default search bar widget.
-                getOrCreateQsbBar();
+                addAppWidgetImpl(
+                        appWidgetId, requestArgs, null,
+                        requestArgs.getWidgetProvider(),
+                        ON_ACTIVITY_RESULT_ANIMATION_DELAY);
             }
             return;
         } else if (requestCode == REQUEST_PICK_WALLPAPER) {
             if (resultCode == RESULT_OK && mWorkspace.isInOverviewMode()) {
                 // User could have free-scrolled between pages before picking a wallpaper; make sure
-                // we move to the closest one now to avoid visual jump.
+                // we move to the closest one now.
                 mWorkspace.setCurrentPage(mWorkspace.getPageNearestToCenterOfScreen());
                 showWorkspace(false);
             }
@@ -733,7 +753,6 @@
         boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
                 requestCode == REQUEST_CREATE_APPWIDGET);
 
-        final boolean workspaceLocked = isWorkspaceLocked();
         // We have special handling for widgets
         if (isWidgetDrop) {
             final int appWidgetId;
@@ -750,86 +769,63 @@
                 Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not " +
                         "returned from the widget configuration activity.");
                 result = RESULT_CANCELED;
-                completeTwoStageWidgetDrop(result, appWidgetId);
+                completeTwoStageWidgetDrop(result, appWidgetId, requestArgs);
                 final Runnable onComplete = new Runnable() {
                     @Override
                     public void run() {
                         exitSpringLoadedDragModeDelayed(false, 0, null);
                     }
                 };
-                if (workspaceLocked) {
-                    // No need to remove the empty screen if we're mid-binding, as the
-                    // the bind will not add the empty screen.
-                    mWorkspace.postDelayed(onComplete, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
-                } else {
-                    mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
-                            ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
-                }
-            } else {
-                if (!workspaceLocked) {
-                    if (mPendingAddInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                        // When the screen id represents an actual screen (as opposed to a rank)
-                        // we make sure that the drop page actually exists.
-                        mPendingAddInfo.screenId =
-                                ensurePendingDropLayoutExists(mPendingAddInfo.screenId);
-                    }
-                    final CellLayout dropLayout = mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
 
-                    dropLayout.setDropPending(true);
-                    final Runnable onComplete = new Runnable() {
-                        @Override
-                        public void run() {
-                            completeTwoStageWidgetDrop(resultCode, appWidgetId);
-                            dropLayout.setDropPending(false);
-                        }
-                    };
-                    mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
-                            ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
-                } else {
-                    PendingAddArguments args = preparePendingAddArgs(requestCode, data, appWidgetId,
-                            mPendingAddInfo);
-                    sPendingAddItem = args;
+                mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
+                        ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
+            } else {
+                if (requestArgs.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                    // When the screen id represents an actual screen (as opposed to a rank)
+                    // we make sure that the drop page actually exists.
+                    requestArgs.screenId =
+                            ensurePendingDropLayoutExists(requestArgs.screenId);
                 }
+                final CellLayout dropLayout =
+                        mWorkspace.getScreenWithId(requestArgs.screenId);
+
+                dropLayout.setDropPending(true);
+                final Runnable onComplete = new Runnable() {
+                    @Override
+                    public void run() {
+                        completeTwoStageWidgetDrop(resultCode, appWidgetId, requestArgs);
+                        dropLayout.setDropPending(false);
+                    }
+                };
+                mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
+                        ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
             }
             return;
         }
 
-        if (requestCode == REQUEST_RECONFIGURE_APPWIDGET) {
+        if (requestCode == REQUEST_RECONFIGURE_APPWIDGET
+                || requestCode == REQUEST_BIND_PENDING_APPWIDGET) {
             if (resultCode == RESULT_OK) {
                 // Update the widget view.
-                PendingAddArguments args = preparePendingAddArgs(requestCode, data,
-                        pendingAddWidgetId, mPendingAddInfo);
-                if (workspaceLocked) {
-                    sPendingAddItem = args;
-                } else {
-                    completeAdd(args);
-                }
+                completeAdd(requestCode, data, pendingAddWidgetId, requestArgs);
             }
             // Leave the widget in the pending state if the user canceled the configure.
             return;
         }
 
-        // The pattern used here is that a user PICKs a specific application,
-        // which, depending on the target, might need to CREATE the actual target.
+        if (requestCode == REQUEST_CREATE_SHORTCUT) {
+            // Handle custom shortcuts created using ACTION_CREATE_SHORTCUT.
+            if (resultCode == RESULT_OK && requestArgs.container != ItemInfo.NO_ID) {
+                completeAdd(requestCode, data, -1, requestArgs);
+                mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
+                        ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
 
-        // For example, the user would PICK_SHORTCUT for "Music playlist", and we
-        // launch over to the Music app to actually CREATE_SHORTCUT.
-        if (resultCode == RESULT_OK && mPendingAddInfo.container != ItemInfo.NO_ID) {
-            final PendingAddArguments args = preparePendingAddArgs(requestCode, data, -1,
-                    mPendingAddInfo);
-            if (isWorkspaceLocked()) {
-                sPendingAddItem = args;
-            } else {
-                completeAdd(args);
+            } else if (resultCode == RESULT_CANCELED) {
                 mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
                         ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
             }
-        } else if (resultCode == RESULT_CANCELED) {
-            mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
-                    ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
         }
         mDragLayer.clearAnimatedView();
-
     }
 
     @Override
@@ -844,22 +840,25 @@
     /** @Override for MNC */
     public void onRequestPermissionsResult(int requestCode, String[] permissions,
             int[] grantResults) {
-        if (requestCode == REQUEST_PERMISSION_CALL_PHONE && sPendingAddItem != null
-                && sPendingAddItem.requestCode == REQUEST_PERMISSION_CALL_PHONE) {
+        PendingRequestArgs pendingArgs = mPendingRequestArgs;
+        if (requestCode == REQUEST_PERMISSION_CALL_PHONE && pendingArgs != null
+                && pendingArgs.getRequestCode() == REQUEST_PERMISSION_CALL_PHONE) {
+            setWaitingForResult(null);
+
             View v = null;
-            CellLayout layout = getCellLayout(sPendingAddItem.container, sPendingAddItem.screenId);
+            CellLayout layout = getCellLayout(pendingArgs.container, pendingArgs.screenId);
             if (layout != null) {
-                v = layout.getChildAt(sPendingAddItem.cellX, sPendingAddItem.cellY);
+                v = layout.getChildAt(pendingArgs.cellX, pendingArgs.cellY);
             }
-            Intent intent = sPendingAddItem.intent;
-            sPendingAddItem = null;
+            Intent intent = pendingArgs.getPendingIntent();
+
             if (grantResults.length > 0
                     && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                startActivity(v, intent, null);
+                startActivitySafely(v, intent, null);
             } else {
                 // TODO: Show a snack bar with link to settings
                 Toast.makeText(this, getString(R.string.msg_no_phone_permission,
-                        getString(R.string.app_name)), Toast.LENGTH_SHORT).show();
+                        getString(R.string.derived_app_name)), Toast.LENGTH_SHORT).show();
             }
         }
         if (mLauncherCallbacks != null) {
@@ -868,19 +867,6 @@
         }
     }
 
-    private PendingAddArguments preparePendingAddArgs(int requestCode, Intent data, int
-            appWidgetId, ItemInfo info) {
-        PendingAddArguments args = new PendingAddArguments();
-        args.requestCode = requestCode;
-        args.intent = data;
-        args.container = info.container;
-        args.screenId = info.screenId;
-        args.cellX = info.cellX;
-        args.cellY = info.cellY;
-        args.appWidgetId = appWidgetId;
-        return args;
-    }
-
     /**
      * Check to see if a given screen id exists. If not, create it at the end, return the new id.
      *
@@ -899,8 +885,9 @@
         }
     }
 
-    @Thunk void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
-        CellLayout cellLayout = mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
+    @Thunk void completeTwoStageWidgetDrop(
+            final int resultCode, final int appWidgetId, final PendingRequestArgs requestArgs) {
+        CellLayout cellLayout = mWorkspace.getScreenWithId(requestArgs.screenId);
         Runnable onCompleteRunnable = null;
         int animationType = 0;
 
@@ -908,13 +895,12 @@
         if (resultCode == RESULT_OK) {
             animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
             final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId,
-                    mPendingAddWidgetInfo);
+                    requestArgs.getWidgetProvider());
             boundWidget = layout;
             onCompleteRunnable = new Runnable() {
                 @Override
                 public void run() {
-                    completeAddAppWidget(appWidgetId, mPendingAddInfo.container,
-                            mPendingAddInfo.screenId, layout, null);
+                    completeAddAppWidget(appWidgetId, requestArgs, layout, null);
                     exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
                             EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
                 }
@@ -924,7 +910,7 @@
             animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
         }
         if (mDragLayer.getAnimatedView() != null) {
-            mWorkspace.animateWidgetDrop(mPendingAddInfo, cellLayout,
+            mWorkspace.animateWidgetDrop(requestArgs, cellLayout,
                     (DragView) mDragLayer.getAnimatedView(), onCompleteRunnable,
                     animationType, boundWidget, true);
         } else if (onCompleteRunnable != null) {
@@ -941,6 +927,10 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onStop();
         }
+
+        if (Utilities.isNycMR1OrAbove()) {
+            mAppWidgetHost.stopListening();
+        }
     }
 
     @Override
@@ -951,6 +941,10 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onStart();
         }
+
+        if (Utilities.isNycMR1OrAbove()) {
+            mAppWidgetHost.startListening();
+        }
     }
 
     @Override
@@ -966,6 +960,7 @@
         }
 
         super.onResume();
+        getUserEventDispatcher().resetElapsedSessionMillis();
 
         // Restore the previous launcher state
         if (mOnResumeState == State.WORKSPACE) {
@@ -975,28 +970,17 @@
             // Don't update the predicted apps if the user is returning to launcher in the apps
             // view after launching an app, as they may be depending on the UI to be static to
             // switch to another app, otherwise, if it was
-            showAppsView(false /* animated */, false /* resetListToTop */,
-                    !launchedFromApp /* updatePredictedApps */, false /* focusSearchBar */);
+            showAppsView(false /* animated */, !launchedFromApp /* updatePredictedApps */,
+                    mAppsView.shouldRestoreImeState() /* focusSearchBar */);
         } else if (mOnResumeState == State.WIDGETS) {
             showWidgetsView(false, false);
         }
         mOnResumeState = State.NONE;
 
-        // Background was set to gradient in onPause(), restore to transparent if in all apps.
-        setWorkspaceBackground(mState == State.WORKSPACE ? WORKSPACE_BACKGROUND_GRADIENT
-                : WORKSPACE_BACKGROUND_TRANSPARENT);
-
         mPaused = false;
-        if (mRestoring || mOnResumeNeedsLoad) {
+        if (mOnResumeNeedsLoad) {
             setWorkspaceLoading(true);
-
-            // If we're starting binding all over again, clear any bind calls we'd postponed in
-            // the past (see waitUntilResume) -- we don't need them since we're starting binding
-            // from scratch again
-            mBindOnResumeCallbacks.clear();
-
-            mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
-            mRestoring = false;
+            mModel.startLoader(getCurrentWorkspaceScreen());
             mOnResumeNeedsLoad = false;
         }
         if (mBindOnResumeCallbacks.size() > 0) {
@@ -1037,7 +1021,6 @@
         if (!isWorkspaceLoading()) {
             getWorkspace().reinflateWidgetsIfNecessary();
         }
-        reinflateQSBIfNecessary();
 
         if (DEBUG_RESUME_TIME) {
             Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime));
@@ -1064,8 +1047,15 @@
         if (!isWorkspaceLoading()) {
             // Process any items that were added while Launcher was away.
             InstallShortcutReceiver.disableAndFlushInstallQueue(this);
+
+            // Refresh shortcuts if the permission changed.
+            mModel.refreshShortcutsIfRequired();
         }
 
+        if (shouldShowDiscoveryBounce()) {
+            mAllAppsController.showDiscoveryBounce();
+        }
+        mIsResumeFromActionScreenOff = false;
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onResume();
         }
@@ -1240,6 +1230,9 @@
                 // Close any open folders
                 closeFolder();
 
+                // Close any shortcuts containers
+                closeShortcutsContainer();
+
                 // Stop resizing any widgets
                 mWorkspace.exitWidgetResizeMode();
 
@@ -1259,7 +1252,8 @@
         return mDefaultKeySsb.toString();
     }
 
-    private void clearTypedText() {
+    @Override
+    public void clearTypedText() {
         mDefaultKeySsb.clear();
         mDefaultKeySsb.clearSpans();
         Selection.setSelection(mDefaultKeySsb, 0);
@@ -1302,47 +1296,32 @@
             mWorkspace.setRestorePage(currentScreen);
         }
 
-        final long pendingAddContainer = savedState.getLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, -1);
-        final long pendingAddScreen = savedState.getLong(RUNTIME_STATE_PENDING_ADD_SCREEN, -1);
-
-        if (pendingAddContainer != ItemInfo.NO_ID && pendingAddScreen > -1) {
-            mPendingAddInfo.container = pendingAddContainer;
-            mPendingAddInfo.screenId = pendingAddScreen;
-            mPendingAddInfo.cellX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_X);
-            mPendingAddInfo.cellY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_CELL_Y);
-            mPendingAddInfo.spanX = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_X);
-            mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
-            AppWidgetProviderInfo info = savedState.getParcelable(
-                    RUNTIME_STATE_PENDING_ADD_WIDGET_INFO);
-            mPendingAddWidgetInfo = info == null ?
-                    null : LauncherAppWidgetProviderInfo.fromProviderInfo(this, info);
-
-            mPendingAddWidgetId = savedState.getInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID);
-            setWaitingForResult(true);
-            mRestoring = true;
+        PendingRequestArgs requestArgs = savedState.getParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS);
+        if (requestArgs != null) {
+            setWaitingForResult(requestArgs);
         }
+
+        mPendingActivityResult = savedState.getParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT);
     }
 
     /**
      * Finds all the views we need and configure them properly.
      */
     private void setupViews() {
-        final DragController dragController = mDragController;
-
         mLauncherView = findViewById(R.id.launcher);
-        mFocusHandler = (FocusIndicatorView) findViewById(R.id.focus_indicator);
         mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
+        mFocusHandler = mDragLayer.getFocusIndicatorHelper();
         mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
-        mWorkspace.setPageSwitchListener(this);
-        mPageIndicators = mDragLayer.findViewById(R.id.page_indicator);
+        mQsbContainer = mDragLayer.findViewById(mDeviceProfile.isVerticalBarLayout()
+                ? R.id.workspace_blocked_row : R.id.qsb_container);
+        mWorkspace.initParentViews(mDragLayer);
 
         mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
-        mWorkspaceBackgroundDrawable = getResources().getDrawable(R.drawable.workspace_bg);
 
         // Setup the drag layer
-        mDragLayer.setup(this, dragController);
+        mDragLayer.setup(this, mDragController, mAllAppsController);
 
         // Setup the hotseat
         mHotseat = (Hotseat) findViewById(R.id.hotseat);
@@ -1356,12 +1335,15 @@
         // Setup the workspace
         mWorkspace.setHapticFeedbackEnabled(false);
         mWorkspace.setOnLongClickListener(this);
-        mWorkspace.setup(dragController);
-        dragController.addDragListener(mWorkspace);
+        mWorkspace.setup(mDragController);
+        // Until the workspace is bound, ensure that we keep the wallpaper offset locked to the
+        // default state, otherwise we will update to the wrong offsets in RTL
+        mWorkspace.lockWallpaperToDefaultPage();
+        mWorkspace.bindAndInitFirstWorkspaceScreen(null /* recycled qsb */);
+        mDragController.addDragListener(mWorkspace);
 
-        // Get the search/delete bar
-        mSearchDropTargetBar = (SearchDropTargetBar)
-                mDragLayer.findViewById(R.id.search_drop_target_bar);
+        // Get the search/delete/uninstall bar
+        mDropTargetBar = (DropTargetBar) mDragLayer.findViewById(R.id.drop_target_bar);
 
         // Setup Apps and Widgets
         mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
@@ -1373,13 +1355,14 @@
         }
 
         // Setup the drag controller (drop targets have to be added in reverse order in priority)
-        dragController.setDragScoller(mWorkspace);
-        dragController.setScrollView(mDragLayer);
-        dragController.setMoveTarget(mWorkspace);
-        dragController.addDropTarget(mWorkspace);
-        if (mSearchDropTargetBar != null) {
-            mSearchDropTargetBar.setup(this, dragController);
-            mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());
+        mDragController.setDragScoller(mWorkspace);
+        mDragController.setScrollView(mDragLayer);
+        mDragController.setMoveTarget(mWorkspace);
+        mDragController.addDropTarget(mWorkspace);
+        mDropTargetBar.setup(mDragController);
+
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+            mAllAppsController.setupViews(mAppsView, mHotseat, mWorkspace);
         }
 
         if (TestingUtils.MEMORY_DUMP_ENABLED) {
@@ -1447,13 +1430,14 @@
 
     /**
      * Sets the all apps button. This method is called from {@link Hotseat}.
+     * TODO: Get rid of this.
      */
     public void setAllAppsButton(View allAppsButton) {
         mAllAppsButton = allAppsButton;
     }
 
-    public View getAllAppsButton() {
-        return mAllAppsButton;
+    public View getStartViewForAllAppsRevealAnimation() {
+        return FeatureFlags.NO_ALL_APPS_ICON ? mWorkspace.getPageIndicator() : mAllAppsButton;
     }
 
     public View getWidgetsButton() {
@@ -1478,7 +1462,7 @@
      * @return A View inflated from layoutResId.
      */
     public View createShortcut(ViewGroup parent, ShortcutInfo info) {
-        BubbleTextView favorite = (BubbleTextView) mInflater.inflate(R.layout.app_icon,
+        BubbleTextView favorite = (BubbleTextView) getLayoutInflater().inflate(R.layout.app_icon,
                 parent, false);
         favorite.applyFromShortcutInfo(info, mIconCache);
         favorite.setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx);
@@ -1493,13 +1477,19 @@
      * @param data The intent describing the shortcut.
      */
     private void completeAddShortcut(Intent data, long container, long screenId, int cellX,
-            int cellY) {
+            int cellY, PendingRequestArgs args) {
         int[] cellXY = mTmpAddItemCellCoordinates;
-        int[] touchXY = mPendingAddInfo.dropPos;
         CellLayout layout = getCellLayout(container, screenId);
 
         ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(this, data);
-        if (info == null) {
+        if (info == null || args.getRequestCode() != REQUEST_CREATE_SHORTCUT ||
+                args.getPendingIntent().getComponent() == null) {
+            return;
+        }
+        if (!PackageManagerHelper.hasPermissionForActivity(
+                this, info.intent, args.getPendingIntent().getComponent().getPackageName())) {
+            // The app is trying to add a shortcut without sufficient permissions
+            Log.e(TAG, "Ignoring malicious intent " + info.intent.toUri(0));
             return;
         }
         final View view = createShortcut(info);
@@ -1522,10 +1512,6 @@
                     true)) {
                 return;
             }
-        } else if (touchXY != null) {
-            // when dragging and dropping, just find the closest free spot
-            int[] result = layout.findNearestVacantArea(touchXY[0], touchXY[1], 1, 1, cellXY);
-            foundCellSpan = (result != null);
         } else {
             foundCellSpan = layout.findCellForSpan(cellXY, 1, 1);
         }
@@ -1537,10 +1523,8 @@
 
         LauncherModel.addItemToDatabase(this, info, container, screenId, cellXY[0], cellXY[1]);
 
-        if (!mRestoring) {
-            mWorkspace.addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1,
-                    isWorkspaceLocked());
-        }
+        mWorkspace.addInScreen(view, container, screenId, cellXY[0], cellXY[1], 1, 1,
+                isWorkspaceLocked());
     }
 
     /**
@@ -1548,10 +1532,9 @@
      *
      * @param appWidgetId The app widget id
      */
-    @Thunk void completeAddAppWidget(int appWidgetId, long container, long screenId,
+    @Thunk void completeAddAppWidget(int appWidgetId, ItemInfo itemInfo,
             AppWidgetHostView hostView, LauncherAppWidgetProviderInfo appWidgetInfo) {
 
-        ItemInfo info = mPendingAddInfo;
         if (appWidgetInfo == null) {
             appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId);
         }
@@ -1562,43 +1545,37 @@
 
         LauncherAppWidgetInfo launcherInfo;
         launcherInfo = new LauncherAppWidgetInfo(appWidgetId, appWidgetInfo.provider);
-        launcherInfo.spanX = info.spanX;
-        launcherInfo.spanY = info.spanY;
-        launcherInfo.minSpanX = info.minSpanX;
-        launcherInfo.minSpanY = info.minSpanY;
+        launcherInfo.spanX = itemInfo.spanX;
+        launcherInfo.spanY = itemInfo.spanY;
+        launcherInfo.minSpanX = itemInfo.minSpanX;
+        launcherInfo.minSpanY = itemInfo.minSpanY;
         launcherInfo.user = mAppWidgetManager.getUser(appWidgetInfo);
 
         LauncherModel.addItemToDatabase(this, launcherInfo,
-                container, screenId, info.cellX, info.cellY);
+                itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY);
 
-        if (!mRestoring) {
-            if (hostView == null) {
-                // Perform actual inflation because we're live
-                launcherInfo.hostView = mAppWidgetHost.createView(this, appWidgetId,
-                        appWidgetInfo);
-            } else {
-                // The AppWidgetHostView has already been inflated and instantiated
-                launcherInfo.hostView = hostView;
-            }
-            launcherInfo.hostView.setVisibility(View.VISIBLE);
-            addAppWidgetToWorkspace(launcherInfo, appWidgetInfo, isWorkspaceLocked());
+        if (hostView == null) {
+            // Perform actual inflation because we're live
+            hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
         }
-        resetAddInfo();
+        hostView.setVisibility(View.VISIBLE);
+        addAppWidgetToWorkspace(hostView, launcherInfo, appWidgetInfo, isWorkspaceLocked());
     }
 
-    private void addAppWidgetToWorkspace(LauncherAppWidgetInfo item,
+    private void addAppWidgetToWorkspace(
+            AppWidgetHostView hostView, LauncherAppWidgetInfo item,
             LauncherAppWidgetProviderInfo appWidgetInfo, boolean insert) {
-        item.hostView.setTag(item);
-        item.onBindAppWidget(this);
+        hostView.setTag(item);
+        item.onBindAppWidget(this, hostView);
 
-        item.hostView.setFocusable(true);
-        item.hostView.setOnFocusChangeListener(mFocusHandler);
+        hostView.setFocusable(true);
+        hostView.setOnFocusChangeListener(mFocusHandler);
 
-        mWorkspace.addInScreen(item.hostView, item.container, item.screenId,
+        mWorkspace.addInScreen(hostView, item.container, item.screenId,
                 item.cellX, item.cellY, item.spanX, item.spanY, insert);
 
         if (!item.isCustomWidget()) {
-            addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
+            addWidgetToAutoAdvanceIfNeeded(hostView, appWidgetInfo);
         }
     }
 
@@ -1613,25 +1590,16 @@
 
                 // Reset AllApps to its initial state only if we are not in the middle of
                 // processing a multi-step drop
-                if (mAppsView != null && mWidgetsView != null &&
-                        mPendingAddInfo.container == ItemInfo.NO_ID) {
+                if (mAppsView != null && mWidgetsView != null && mPendingRequestArgs == null) {
                     if (!showWorkspace(false)) {
                         // If we are already on the workspace, then manually reset all apps
                         mAppsView.reset();
                     }
                 }
+                mIsResumeFromActionScreenOff = true;
             } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
                 mUserPresent = true;
                 updateAutoAdvanceState();
-            } else if (ENABLE_DEBUG_INTENTS && DebugIntents.DELETE_DATABASE.equals(action)) {
-                mModel.resetLoadedState(false, true);
-                mModel.startLoader(PagedView.INVALID_RESTORE_PAGE,
-                        LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE);
-            } else if (ENABLE_DEBUG_INTENTS && DebugIntents.MIGRATE_DATABASE.equals(action)) {
-                mModel.resetLoadedState(false, true);
-                mModel.startLoader(PagedView.INVALID_RESTORE_PAGE,
-                        LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
-                                | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
             }
         }
     };
@@ -1644,11 +1612,6 @@
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         filter.addAction(Intent.ACTION_USER_PRESENT);
-        // For handling managed profiles
-        if (ENABLE_DEBUG_INTENTS) {
-            filter.addAction(DebugIntents.DELETE_DATABASE);
-            filter.addAction(DebugIntents.MIGRATE_DATABASE);
-        }
         registerReceiver(mReceiver, filter);
         FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
         mAttached = true;
@@ -1728,11 +1691,11 @@
         if (autoAdvanceRunning != mAutoAdvanceRunning) {
             mAutoAdvanceRunning = autoAdvanceRunning;
             if (autoAdvanceRunning) {
-                long delay = mAutoAdvanceTimeLeft == -1 ? mAdvanceInterval : mAutoAdvanceTimeLeft;
+                long delay = mAutoAdvanceTimeLeft == -1 ? ADVANCE_INTERVAL : mAutoAdvanceTimeLeft;
                 sendAdvanceMessage(delay);
             } else {
                 if (!mWidgetsToAdvance.isEmpty()) {
-                    mAutoAdvanceTimeLeft = Math.max(0, mAdvanceInterval -
+                    mAutoAdvanceTimeLeft = Math.max(0, ADVANCE_INTERVAL -
                             (System.currentTimeMillis() - mAutoAdvanceSentTime));
                 }
                 mHandler.removeMessages(ADVANCE_MSG);
@@ -1749,7 +1712,7 @@
                 int i = 0;
                 for (View key: mWidgetsToAdvance.keySet()) {
                     final View v = key.findViewById(mWidgetsToAdvance.get(key).autoAdvanceViewId);
-                    final int delay = mAdvanceStagger * i;
+                    final int delay = ADVANCE_STAGGER * i;
                     if (v instanceof Advanceable) {
                         mHandler.postDelayed(new Runnable() {
                            public void run() {
@@ -1759,7 +1722,7 @@
                     }
                     i++;
                 }
-                sendAdvanceMessage(mAdvanceInterval);
+                sendAdvanceMessage(ADVANCE_INTERVAL);
             }
             return true;
         }
@@ -1803,6 +1766,10 @@
         return mWorkspace;
     }
 
+    public View getQsbContainer() {
+        return mQsbContainer;
+    }
+
     public Hotseat getHotseat() {
         return mHotseat;
     }
@@ -1811,8 +1778,8 @@
         return mOverviewPanel;
     }
 
-    public SearchDropTargetBar getSearchDropTargetBar() {
-        return mSearchDropTargetBar;
+    public DropTargetBar getDropTargetBar() {
+        return mDropTargetBar;
     }
 
     public LauncherAppWidgetHost getAppWidgetHost() {
@@ -1823,7 +1790,7 @@
         return mModel;
     }
 
-    protected SharedPreferences getSharedPrefs() {
+    public SharedPreferences getSharedPrefs() {
         return mSharedPrefs;
     }
 
@@ -1835,7 +1802,7 @@
         getWindow().closeAllPanels();
 
         // Whatever we were doing is hereby canceled.
-        setWaitingForResult(false);
+        setWaitingForResult(null);
     }
 
     @Override
@@ -1846,11 +1813,14 @@
         }
         super.onNewIntent(intent);
 
-        // Close the menu
-        Folder openFolder = mWorkspace.getOpenFolder();
         boolean alreadyOnHome = mHasFocus && ((intent.getFlags() &
                 Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
                 != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
+
+        // Check this condition before handling isActionMain, as this will get reset.
+        boolean shouldMoveToDefaultScreen = alreadyOnHome &&
+                mState == State.WORKSPACE && getTopFloatingView() == null;
+
         boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
         if (isActionMain) {
             // also will cancel mWaitingForResult.
@@ -1864,6 +1834,7 @@
             mWorkspace.exitWidgetResizeMode();
 
             closeFolder(alreadyOnHome);
+            closeShortcutsContainer(alreadyOnHome);
             exitSpringLoadedDragMode();
 
             // If we are already on home, then just animate back to the workspace,
@@ -1904,10 +1875,10 @@
         // as slow logic in the callbacks eat into the time the scroller expects for the snapToPage
         // animation.
         if (isActionMain) {
-            boolean moveToDefaultScreen = mLauncherCallbacks != null ?
+            boolean callbackAllowsMoveToDefaultScreen = mLauncherCallbacks != null ?
                     mLauncherCallbacks.shouldMoveToDefaultScreenOnHomeIntent() : true;
-            if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() &&
-                    openFolder == null && moveToDefaultScreen) {
+            if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()
+                    && callbackAllowsMoveToDefaultScreen) {
 
                 // We use this flag to suppress noisy callbacks above custom content state
                 // from onResume.
@@ -1938,40 +1909,26 @@
 
     @Override
     protected void onSaveInstanceState(Bundle outState) {
-        // Catches the case where our activity is created and immediately destroyed and our views
-        // are not yet fully bound. In this case, we can't trust the state of our activity and
-        // instead save our previous state (which hasn't yet been consumed / applied, a fact we
-        // know as it's not null)
-        if (isWorkspaceLoading() && mSavedState != null) {
-            outState.putAll(mSavedState);
-            return;
-        }
-
         if (mWorkspace.getChildCount() > 0) {
             outState.putInt(RUNTIME_STATE_CURRENT_SCREEN,
                     mWorkspace.getCurrentPageOffsetFromCustomContent());
+
         }
         super.onSaveInstanceState(outState);
 
         outState.putInt(RUNTIME_STATE, mState.ordinal());
         // We close any open folder since it will not be re-opened, and we need to make sure
         // this state is reflected.
+        // TODO: Move folderInfo.isOpened out of the model and make it a UI state.
         closeFolder(false);
+        closeShortcutsContainer(false);
 
-        if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screenId > -1 &&
-                mWaitingForResult) {
-            outState.putLong(RUNTIME_STATE_PENDING_ADD_CONTAINER, mPendingAddInfo.container);
-            outState.putLong(RUNTIME_STATE_PENDING_ADD_SCREEN, mPendingAddInfo.screenId);
-            outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_X, mPendingAddInfo.cellX);
-            outState.putInt(RUNTIME_STATE_PENDING_ADD_CELL_Y, mPendingAddInfo.cellY);
-            outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_X, mPendingAddInfo.spanX);
-            outState.putInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y, mPendingAddInfo.spanY);
-            outState.putParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO, mPendingAddWidgetInfo);
-            outState.putInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID, mPendingAddWidgetId);
+        if (mPendingRequestArgs != null) {
+            outState.putParcelable(RUNTIME_STATE_PENDING_REQUEST_ARGS, mPendingRequestArgs);
         }
-
-        // Save the current widgets tray?
-        // TODO(hyunyoungs)
+        if (mPendingActivityResult != null) {
+            outState.putParcelable(RUNTIME_STATE_PENDING_ACTIVITY_RESULT, mPendingActivityResult);
+        }
 
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onSaveInstanceState(outState);
@@ -1986,15 +1943,18 @@
         mHandler.removeMessages(ADVANCE_MSG);
         mHandler.removeMessages(0);
         mWorkspace.removeCallbacks(mBuildLayersRunnable);
+        mWorkspace.removeFolderListeners();
 
         // Stop callbacks from LauncherModel
-        LauncherAppState app = (LauncherAppState.getInstance());
-
         // It's possible to receive onDestroy after a new Launcher activity has
         // been created. In this case, don't interfere with the new Launcher.
         if (mModel.isCurrentCallbacks(this)) {
             mModel.stopLoader();
-            app.setLauncher(null);
+            LauncherAppState.getInstance().setLauncher(null);
+        }
+
+        if (mRotationPrefChangeHandler != null) {
+            mSharedPrefs.unregisterOnSharedPreferenceChangeListener(mRotationPrefChangeHandler);
         }
 
         try {
@@ -2008,13 +1968,10 @@
 
         TextKeyListener.getInstance().release();
 
-        unregisterReceiver(mCloseSystemDialogsReceiver);
+        ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
+                .removeAccessibilityStateChangeListener(this);
 
-        mDragLayer.clearAllResizeFrames();
-        ((ViewGroup) mWorkspace.getParent()).removeAllViews();
-        mWorkspace.removeAllWorkspaceScreens();
-        mWorkspace = null;
-        mDragController = null;
+        unregisterReceiver(mUiBroadcastReceiver);
 
         LauncherAnimUtils.onDestroyActivity();
 
@@ -2023,20 +1980,22 @@
         }
     }
 
+    public LauncherAccessibilityDelegate getAccessibilityDelegate() {
+        return mAccessibilityDelegate;
+    }
+
     public DragController getDragController() {
         return mDragController;
     }
 
     @Override
-    public void startActivityForResult(Intent intent, int requestCode) {
-        onStartForResult(requestCode);
-        super.startActivityForResult(intent, requestCode);
+    public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
+        super.startActivityForResult(intent, requestCode, options);
     }
 
     @Override
     public void startIntentSenderForResult (IntentSender intent, int requestCode,
             Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options) {
-        onStartForResult(requestCode);
         try {
             super.startIntentSenderForResult(intent, requestCode,
                 fillInIntent, flagsMask, flagsValues, extraFlags, options);
@@ -2045,12 +2004,6 @@
         }
     }
 
-    private void onStartForResult(int requestCode) {
-        if (requestCode >= 0) {
-            setWaitingForResult(true);
-        }
-    }
-
     /**
      * Indicates that we want global search for this activity by setting the globalSearch
      * argument for {@link #startSearch} to true.
@@ -2067,15 +2020,11 @@
             appSearchData = new Bundle();
             appSearchData.putString("source", "launcher-search");
         }
-        Rect sourceBounds = new Rect();
-        if (mSearchDropTargetBar != null) {
-            sourceBounds = mSearchDropTargetBar.getSearchBarBounds();
-        }
 
-        boolean clearTextImmediately = startSearch(initialQuery, selectInitialQuery,
-                appSearchData, sourceBounds);
-        if (clearTextImmediately) {
-            clearTypedText();
+        if (mLauncherCallbacks == null ||
+                !mLauncherCallbacks.startSearch(initialQuery, selectInitialQuery, appSearchData)) {
+            // Starting search from the callbacks failed. Start the default global search.
+            startGlobalSearch(initialQuery, selectInitialQuery, appSearchData, null);
         }
 
         // We need to show the workspace after starting the search
@@ -2083,28 +2032,9 @@
     }
 
     /**
-     * Start a text search.
-     *
-     * @return {@code true} if the search will start immediately, so any further keypresses
-     * will be handled directly by the search UI. {@code false} if {@link Launcher} should continue
-     * to buffer keypresses.
-     */
-    public boolean startSearch(String initialQuery,
-            boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
-        if (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch()) {
-            return mLauncherCallbacks.startSearch(initialQuery, selectInitialQuery, appSearchData,
-                    sourceBounds);
-        }
-
-        startGlobalSearch(initialQuery, selectInitialQuery,
-                appSearchData, sourceBounds);
-        return false;
-    }
-
-    /**
      * Starts the global search activity. This code is a copied from SearchManager
      */
-    private void startGlobalSearch(String initialQuery,
+    public void startGlobalSearch(String initialQuery,
             boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
         final SearchManager searchManager =
             (SearchManager) getSystemService(Context.SEARCH_SERVICE);
@@ -2162,7 +2092,7 @@
     }
 
     public boolean isWorkspaceLocked() {
-        return mWorkspaceLoading || mWaitingForResult;
+        return mWorkspaceLoading || mPendingRequestArgs != null;
     }
 
     public boolean isWorkspaceLoading() {
@@ -2177,9 +2107,9 @@
         }
     }
 
-    private void setWaitingForResult(boolean value) {
+    private void setWaitingForResult(PendingRequestArgs args) {
         boolean isLocked = isWorkspaceLocked();
-        mWaitingForResult = value;
+        mPendingRequestArgs = args;
         if (isLocked != isWorkspaceLocked()) {
             onWorkspaceLockedChanged();
         }
@@ -2191,34 +2121,23 @@
         }
     }
 
-    private void resetAddInfo() {
-        mPendingAddInfo.container = ItemInfo.NO_ID;
-        mPendingAddInfo.screenId = -1;
-        mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
-        mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
-        mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = 1;
-        mPendingAddInfo.dropPos = null;
-    }
-
-    void addAppWidgetFromDropImpl(final int appWidgetId, final ItemInfo info, final
-            AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo) {
+    void addAppWidgetFromDropImpl(int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget,
+            LauncherAppWidgetProviderInfo appWidgetInfo) {
         if (LOGD) {
             Log.d(TAG, "Adding widget from drop");
         }
         addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0);
     }
 
-    void addAppWidgetImpl(final int appWidgetId, final ItemInfo info,
-            final AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo,
+    void addAppWidgetImpl(int appWidgetId, ItemInfo info,
+            AppWidgetHostView boundWidget, LauncherAppWidgetProviderInfo appWidgetInfo,
             int delay) {
         if (appWidgetInfo.configure != null) {
-            mPendingAddWidgetInfo = appWidgetInfo;
-            mPendingAddWidgetId = appWidgetId;
+            setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, appWidgetInfo, info));
 
             // Launch over to configure widget, if needed
             mAppWidgetManager.startConfigActivity(appWidgetInfo, appWidgetId, this,
                     mAppWidgetHost, REQUEST_CREATE_APPWIDGET);
-
         } else {
             // Otherwise just add it
             Runnable onComplete = new Runnable() {
@@ -2229,8 +2148,7 @@
                             null);
                 }
             };
-            completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget,
-                    appWidgetInfo);
+            completeAddAppWidget(appWidgetId, info, boundWidget, appWidgetInfo);
             mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false);
         }
     }
@@ -2243,17 +2161,22 @@
 
     public void addPendingItem(PendingAddItemInfo info, long container, long screenId,
             int[] cell, int spanX, int spanY) {
+        info.container = container;
+        info.screenId = screenId;
+        if (cell != null) {
+            info.cellX = cell[0];
+            info.cellY = cell[1];
+        }
+        info.spanX = spanX;
+        info.spanY = spanY;
+
         switch (info.itemType) {
             case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
             case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
-                int span[] = new int[2];
-                span[0] = spanX;
-                span[1] = spanY;
-                addAppWidgetFromDrop((PendingAddWidgetInfo) info,
-                        container, screenId, cell, span);
+                addAppWidgetFromDrop((PendingAddWidgetInfo) info);
                 break;
             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
-                processShortcutFromDrop(info.componentName, container, screenId, cell);
+                processShortcutFromDrop(info);
                 break;
             default:
                 throw new IllegalStateException("Unknown item type: " + info.itemType);
@@ -2262,53 +2185,17 @@
 
     /**
      * Process a shortcut drop.
-     *
-     * @param componentName The name of the component
-     * @param screenId The ID of the screen where it should be added
-     * @param cell The cell it should be added to, optional
      */
-    private void processShortcutFromDrop(ComponentName componentName, long container, long screenId,
-            int[] cell) {
-        resetAddInfo();
-        mPendingAddInfo.container = container;
-        mPendingAddInfo.screenId = screenId;
-        mPendingAddInfo.dropPos = null;
-
-        if (cell != null) {
-            mPendingAddInfo.cellX = cell[0];
-            mPendingAddInfo.cellY = cell[1];
-        }
-
-        Intent createShortcutIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
-        createShortcutIntent.setComponent(componentName);
-        Utilities.startActivityForResultSafely(this, createShortcutIntent, REQUEST_CREATE_SHORTCUT);
+    private void processShortcutFromDrop(PendingAddItemInfo info) {
+        Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT).setComponent(info.componentName);
+        setWaitingForResult(PendingRequestArgs.forIntent(REQUEST_CREATE_SHORTCUT, intent, info));
+        Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT);
     }
 
     /**
      * Process a widget drop.
-     *
-     * @param info The PendingAppWidgetInfo of the widget being added.
-     * @param screenId The ID of the screen where it should be added
-     * @param cell The cell it should be added to, optional
      */
-    private void addAppWidgetFromDrop(PendingAddWidgetInfo info, long container, long screenId,
-            int[] cell, int[] span) {
-        resetAddInfo();
-        mPendingAddInfo.container = info.container = container;
-        mPendingAddInfo.screenId = info.screenId = screenId;
-        mPendingAddInfo.dropPos = null;
-        mPendingAddInfo.minSpanX = info.minSpanX;
-        mPendingAddInfo.minSpanY = info.minSpanY;
-
-        if (cell != null) {
-            mPendingAddInfo.cellX = cell[0];
-            mPendingAddInfo.cellY = cell[1];
-        }
-        if (span != null) {
-            mPendingAddInfo.spanX = span[0];
-            mPendingAddInfo.spanY = span[1];
-        }
-
+    private void addAppWidgetFromDrop(PendingAddWidgetInfo info) {
         AppWidgetHostView hostView = info.boundWidget;
         int appWidgetId;
         if (hostView != null) {
@@ -2334,11 +2221,11 @@
             if (success) {
                 addAppWidgetFromDropImpl(appWidgetId, info, null, info.info);
             } else {
-                mPendingAddWidgetInfo = info.info;
+                setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, info.info, info));
                 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
-                mAppWidgetManager.getUser(mPendingAddWidgetInfo)
+                mAppWidgetManager.getUser(info.info)
                     .addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
                 // TODO: we need to make sure that this accounts for the options bundle.
                 // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
@@ -2355,7 +2242,6 @@
         // Update the model
         LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId,
                 cellX, cellY);
-        sFolders.put(folderInfo.id, folderInfo);
 
         // Create the view
         FolderIcon newFolder =
@@ -2375,12 +2261,12 @@
      * @param itemInfo the {@link ItemInfo} for this view.
      * @param deleteFromDb whether or not to delete this item from the db.
      */
-    public boolean removeItem(View v, ItemInfo itemInfo, boolean deleteFromDb) {
+    public boolean removeItem(View v, final ItemInfo itemInfo, boolean deleteFromDb) {
         if (itemInfo instanceof ShortcutInfo) {
             // Remove the shortcut from the folder before removing it from launcher
-            FolderInfo folderInfo = sFolders.get(itemInfo.container);
-            if (folderInfo != null) {
-                folderInfo.remove((ShortcutInfo) itemInfo);
+            View folderIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container);
+            if (folderIcon instanceof FolderIcon) {
+                ((FolderInfo) folderIcon.getTag()).remove((ShortcutInfo) itemInfo, true);
             } else {
                 mWorkspace.removeWorkspaceItem(v);
             }
@@ -2389,7 +2275,9 @@
             }
         } else if (itemInfo instanceof FolderInfo) {
             final FolderInfo folderInfo = (FolderInfo) itemInfo;
-            unbindFolder(folderInfo);
+            if (v instanceof FolderIcon) {
+                ((FolderIcon) v).removeListeners();
+            }
             mWorkspace.removeWorkspaceItem(v);
             if (deleteFromDb) {
                 LauncherModel.deleteFolderAndContentsFromDatabase(this, folderInfo);
@@ -2397,12 +2285,10 @@
         } else if (itemInfo instanceof LauncherAppWidgetInfo) {
             final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo;
             mWorkspace.removeWorkspaceItem(v);
-            removeWidgetToAutoAdvance(widgetInfo.hostView);
-            widgetInfo.hostView = null;
+            removeWidgetToAutoAdvance(v);
             if (deleteFromDb) {
                 deleteWidgetInfo(widgetInfo);
             }
-
         } else {
             return false;
         }
@@ -2410,18 +2296,11 @@
     }
 
     /**
-     * Unbinds any launcher references to the folder.
-     */
-    private void unbindFolder(FolderInfo folder) {
-        sFolders.remove(folder.id);
-    }
-
-    /**
      * Deletes the widget info and the widget id.
      */
     private void deleteWidgetInfo(final LauncherAppWidgetInfo widgetInfo) {
         final LauncherAppWidgetHost appWidgetHost = getAppWidgetHost();
-        if (appWidgetHost != null && !widgetInfo.isCustomWidget() && widgetInfo.isWidgetIdValid()) {
+        if (appWidgetHost != null && !widgetInfo.isCustomWidget() && widgetInfo.isWidgetIdAllocated()) {
             // Deleting an app widget ID is a void call but writes to disk before returning
             // to the caller...
             new AsyncTask<Void, Void, Void>() {
@@ -2468,7 +2347,9 @@
             return;
         }
 
-        if (isAppsViewVisible()) {
+        if (getOpenShortcutsContainer() != null) {
+            closeShortcutsContainer();
+        } else if (isAppsViewVisible()) {
             showWorkspace(true);
         } else if (isWidgetsViewVisible())  {
             showOverviewMode(true);
@@ -2490,19 +2371,6 @@
     }
 
     /**
-     * Re-listen when widget host is reset.
-     */
-    @Override
-    public void onAppWidgetHostReset() {
-        if (mAppWidgetHost != null) {
-            mAppWidgetHost.startListening();
-        }
-
-        // Recreate the QSB, as the widget has been reset.
-        bindSearchProviderChanged();
-    }
-
-    /**
      * Launches the intent referred by the clicked shortcut.
      *
      * @param v The view representing the clicked shortcut.
@@ -2527,8 +2395,10 @@
 
         if (v instanceof CellLayout) {
             if (mWorkspace.isInOverviewMode()) {
-                showWorkspace(mWorkspace.indexOfChild(v), true);
+                mWorkspace.snapToPageFromOverView(mWorkspace.indexOfChild(v));
+                showWorkspace(true);
             }
+            return;
         }
 
         Object tag = v.getTag();
@@ -2538,7 +2408,8 @@
             if (v instanceof FolderIcon) {
                 onClickFolderIcon(v);
             }
-        } else if (v == mAllAppsButton) {
+        } else if ((FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && v instanceof PageIndicator) ||
+                (v == mAllAppsButton && mAllAppsButton != null)) {
             onClickAllAppsButton(v);
         } else if (tag instanceof AppInfo) {
             startAppShortcutOrInfoActivity(v);
@@ -2565,16 +2436,30 @@
 
         final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
         if (v.isReadyForClickSetup()) {
-            int widgetId = info.appWidgetId;
-            LauncherAppWidgetProviderInfo appWidgetInfo =
-                    mAppWidgetManager.getLauncherAppWidgetInfo(widgetId);
-            if (appWidgetInfo != null) {
-                mPendingAddWidgetInfo = appWidgetInfo;
-                mPendingAddInfo.copyFrom(info);
-                mPendingAddWidgetId = widgetId;
+            if (info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+                if (!info.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
+                    // This should not happen, as we make sure that an Id is allocated during bind.
+                    return;
+                }
+                LauncherAppWidgetProviderInfo appWidgetInfo =
+                        mAppWidgetManager.findProvider(info.providerName, info.user);
+                if (appWidgetInfo != null) {
+                    setWaitingForResult(PendingRequestArgs
+                            .forWidgetInfo(info.appWidgetId, appWidgetInfo, info));
 
-                AppWidgetManagerCompat.getInstance(this).startConfigActivity(appWidgetInfo,
-                        info.appWidgetId, this, mAppWidgetHost, REQUEST_RECONFIGURE_APPWIDGET);
+                    Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
+                    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, info.appWidgetId);
+                    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, appWidgetInfo.provider);
+                    mAppWidgetManager.getUser(appWidgetInfo)
+                            .addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
+                    startActivityForResult(intent, REQUEST_BIND_PENDING_APPWIDGET);
+                }
+            } else {
+                LauncherAppWidgetProviderInfo appWidgetInfo =
+                        mAppWidgetManager.getLauncherAppWidgetInfo(info.appWidgetId);
+                if (appWidgetInfo != null) {
+                    startRestoredWidgetReconfigActivity(appWidgetInfo, info);
+                }
             }
         } else if (info.installProgress < 0) {
             // The install has not been queued
@@ -2592,6 +2477,13 @@
         }
     }
 
+    private void startRestoredWidgetReconfigActivity(
+            LauncherAppWidgetProviderInfo provider, LauncherAppWidgetInfo info) {
+        setWaitingForResult(PendingRequestArgs.forWidgetInfo(info.appWidgetId, provider, info));
+        mAppWidgetManager.startConfigActivity(provider,
+                info.appWidgetId, this, mAppWidgetHost, REQUEST_RECONFIGURE_APPWIDGET);
+    }
+
     /**
      * Event handler for the "grid" button that appears on the home screen, which
      * enters all apps mode.
@@ -2601,19 +2493,19 @@
     protected void onClickAllAppsButton(View v) {
         if (LOGD) Log.d(TAG, "onClickAllAppsButton");
         if (!isAppsViewVisible()) {
-            showAppsView(true /* animated */, false /* resetListToTop */,
-                    true /* updatePredictedApps */, false /* focusSearchBar */);
-
-            if (mLauncherCallbacks != null) {
-                mLauncherCallbacks.onClickAllAppsButton(v);
-            }
+            getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.TAP,
+                    LauncherLogProto.ALL_APPS_BUTTON);
+            showAppsView(true /* animated */, true /* updatePredictedApps */,
+                    false /* focusSearchBar */);
         }
     }
 
     protected void onLongClickAllAppsButton(View v) {
         if (LOGD) Log.d(TAG, "onLongClickAllAppsButton");
         if (!isAppsViewVisible()) {
-            showAppsView(true /* animated */, false /* resetListToTop */,
+            getUserEventDispatcher().logActionOnControl(LauncherLogProto.Action.LONGPRESS,
+                    LauncherLogProto.ALL_APPS_BUTTON);
+            showAppsView(true /* animated */,
                     true /* updatePredictedApps */, true /* focusSearchBar */);
         }
     }
@@ -2651,13 +2543,24 @@
         final ShortcutInfo shortcut = (ShortcutInfo) tag;
 
         if (shortcut.isDisabled != 0) {
-            if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SUSPENDED) != 0
-                || (shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_QUIET_USER) != 0) {
-                // Launch activity anyway, framework will tell the user why the app is suspended.
+            if ((shortcut.isDisabled &
+                    ~ShortcutInfo.FLAG_DISABLED_SUSPENDED &
+                    ~ShortcutInfo.FLAG_DISABLED_QUIET_USER) == 0) {
+                // If the app is only disabled because of the above flags, launch activity anyway.
+                // Framework will tell the user why the app is suspended.
             } else {
+                if (!TextUtils.isEmpty(shortcut.disabledMessage)) {
+                    // Use a message specific to this shortcut, if it has one.
+                    Toast.makeText(this, shortcut.disabledMessage, Toast.LENGTH_SHORT).show();
+                    return;
+                }
+                // Otherwise just use a generic error message.
                 int error = R.string.activity_not_available;
                 if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SAFEMODE) != 0) {
                     error = R.string.safemode_shortcut_error;
+                } else if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_BY_PUBLISHER) != 0 ||
+                        (shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_LOCKED_USER) != 0) {
+                    error = R.string.shortcut_not_available;
                 }
                 Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
                 return;
@@ -2680,33 +2583,16 @@
 
         // Start activities
         startAppShortcutOrInfoActivity(v);
-
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onClickAppShortcut(v);
-        }
     }
 
-    @Thunk void startAppShortcutOrInfoActivity(View v) {
-        Object tag = v.getTag();
-        final ShortcutInfo shortcut;
-        final Intent intent;
-        if (tag instanceof ShortcutInfo) {
-            shortcut = (ShortcutInfo) tag;
-            intent = shortcut.intent;
-            int[] pos = new int[2];
-            v.getLocationOnScreen(pos);
-            intent.setSourceBounds(new Rect(pos[0], pos[1],
-                    pos[0] + v.getWidth(), pos[1] + v.getHeight()));
-
-        } else if (tag instanceof AppInfo) {
-            shortcut = null;
-            intent = ((AppInfo) tag).intent;
-        } else {
-            throw new IllegalArgumentException("Input must be a Shortcut or AppInfo");
+    private void startAppShortcutOrInfoActivity(View v) {
+        ItemInfo item = (ItemInfo) v.getTag();
+        Intent intent = item.getIntent();
+        if (intent == null) {
+            throw new IllegalArgumentException("Input must have a valid intent");
         }
-
-        boolean success = startActivitySafely(v, intent, tag);
-        mStats.recordLaunch(v, intent, shortcut);
+        boolean success = startActivitySafely(v, intent, item);
+        getUserEventDispatcher().logAppLaunch(v, intent);
 
         if (success && v instanceof BubbleTextView) {
             mWaitingForResume = (BubbleTextView) v;
@@ -2725,42 +2611,10 @@
             throw new IllegalArgumentException("Input must be a FolderIcon");
         }
 
-        // TODO(sunnygoyal): Re-evaluate this code.
         FolderIcon folderIcon = (FolderIcon) v;
-        final FolderInfo info = folderIcon.getFolderInfo();
-        Folder openFolder = mWorkspace.getFolderForTag(info);
-
-        // If the folder info reports that the associated folder is open, then verify that
-        // it is actually opened. There have been a few instances where this gets out of sync.
-        if (info.opened && openFolder == null) {
-            Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: "
-                    + info.screenId + " (" + info.cellX + ", " + info.cellY + ")");
-            info.opened = false;
-        }
-
-        if (!info.opened && !folderIcon.getFolder().isDestroyed()) {
-            // Close any open folder
-            closeFolder();
+        if (!folderIcon.getFolderInfo().opened && !folderIcon.getFolder().isDestroyed()) {
             // Open the requested folder
             openFolder(folderIcon);
-        } else {
-            // Find the open folder...
-            int folderScreen;
-            if (openFolder != null) {
-                folderScreen = mWorkspace.getPageForView(openFolder);
-                // .. and close it
-                closeFolder(openFolder, true);
-                if (folderScreen != mWorkspace.getCurrentPage()) {
-                    // Close any folder open on the current screen
-                    closeFolder();
-                    // Pull the folder onto this screen
-                    openFolder(folderIcon);
-                }
-            }
-        }
-
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onClickFolderIcon(v);
         }
     }
 
@@ -2768,15 +2622,12 @@
      * Event handler for the (Add) Widgets button that appears after a long press
      * on the home screen.
      */
-    protected void onClickAddWidgetButton(View view) {
+    public void onClickAddWidgetButton(View view) {
         if (LOGD) Log.d(TAG, "onClickAddWidgetButton");
         if (mIsSafeModeEnabled) {
             Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
         } else {
             showWidgetsView(true /* animated */, true /* resetPageToZero */);
-            if (mLauncherCallbacks != null) {
-                mLauncherCallbacks.onClickAddWidgetButton(view);
-            }
         }
     }
 
@@ -2784,35 +2635,38 @@
      * Event handler for the wallpaper picker button that appears after a long press
      * on the home screen.
      */
-    protected void onClickWallpaperPicker(View v) {
+    public void onClickWallpaperPicker(View v) {
         if (!Utilities.isWallapaperAllowed(this)) {
             Toast.makeText(this, R.string.msg_disabled_by_admin, Toast.LENGTH_SHORT).show();
             return;
         }
 
-        if (LOGD) Log.d(TAG, "onClickWallpaperPicker");
+        String pickerPackage = getString(R.string.wallpaper_picker_package);
+        if (TextUtils.isEmpty(pickerPackage)) {
+            pickerPackage =  PackageManagerHelper.getWallpaperPickerPackage(getPackageManager());
+        }
+
         int pageScroll = mWorkspace.getScrollForPage(mWorkspace.getPageNearestToCenterOfScreen());
         float offset = mWorkspace.mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
-        startActivityForResult(new Intent(Intent.ACTION_SET_WALLPAPER).setPackage(getPackageName())
-                        .putExtra(WallpaperPickerActivity.EXTRA_WALLPAPER_OFFSET, offset),
-                REQUEST_PICK_WALLPAPER);
 
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onClickWallpaperPicker(v);
-        }
+        setWaitingForResult(new PendingRequestArgs(new ItemInfo()));
+        Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
+                .setPackage(pickerPackage)
+                .putExtra(Utilities.EXTRA_WALLPAPER_OFFSET, offset);
+        intent.setSourceBounds(getViewBounds(v));
+        startActivityForResult(intent, REQUEST_PICK_WALLPAPER, getActivityLaunchOptions(v));
     }
 
     /**
      * Event handler for a click on the settings button that appears after a long press
      * on the home screen.
      */
-    protected void onClickSettingsButton(View v) {
+    public void onClickSettingsButton(View v) {
         if (LOGD) Log.d(TAG, "onClickSettingsButton");
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onClickSettingsButton(v);
-        } else {
-            startActivity(new Intent(this, SettingsActivity.class));
-        }
+        Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
+                .setPackage(getPackageName());
+        intent.setSourceBounds(getViewBounds(v));
+        startActivity(intent, getActivityLaunchOptions(v));
     }
 
     public View.OnTouchListener getHapticFeedbackTouchListener() {
@@ -2831,16 +2685,17 @@
         return mHapticFeedbackTouchListener;
     }
 
-    public void onDragStarted(View view) {
+    @Override
+    public void onAccessibilityStateChanged(boolean enabled) {
+        mDragLayer.onAccessibilityStateChanged(enabled);
+    }
+
+    public void onDragStarted() {
         if (isOnCustomContent()) {
             // Custom content screen doesn't participate in drag and drop. If on custom
             // content screen, move to default.
             moveWorkspaceToDefaultScreen();
         }
-
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onDragStarted(view);
-        }
     }
 
     /**
@@ -2881,139 +2736,122 @@
         }
     }
 
-    void startApplicationDetailsActivity(ComponentName componentName, UserHandleCompat user) {
+    private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
         try {
-            LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
-            launcherApps.showAppDetailsForProfile(componentName, user);
-        } catch (SecurityException e) {
-            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
-            Log.e(TAG, "Launcher does not have permission to launch settings");
-        } catch (ActivityNotFoundException e) {
-            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
-            Log.e(TAG, "Unable to launch settings");
-        }
-    }
+            StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
+            try {
+                // Temporarily disable deathPenalty on all default checks. For eg, shortcuts
+                // containing file Uri's would cause a crash as penaltyDeathOnFileUriExposure
+                // is enabled by default on NYC.
+                StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll()
+                        .penaltyLog().build());
 
-    // returns true if the activity was started
-    boolean startApplicationUninstallActivity(ComponentName componentName, int flags,
-            UserHandleCompat user) {
-        if ((flags & AppInfo.DOWNLOADED_FLAG) == 0) {
-            // System applications cannot be installed. For now, show a toast explaining that.
-            // We may give them the option of disabling apps this way.
-            int messageId = R.string.uninstall_system_app_text;
-            Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show();
-            return false;
-        } else {
-            String packageName = componentName.getPackageName();
-            String className = componentName.getClassName();
-            Intent intent = new Intent(
-                    Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                    Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-            if (user != null) {
-                user.addToIntent(intent, Intent.EXTRA_USER);
-            }
-            startActivity(intent);
-            return true;
-        }
-    }
-
-    private boolean startActivity(View v, Intent intent, Object tag) {
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        try {
-            // Only launch using the new animation if the shortcut has not opted out (this is a
-            // private contract between launcher and may be ignored in the future).
-            boolean useLaunchAnimation = (v != null) &&
-                    !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
-            LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
-            UserManagerCompat userManager = UserManagerCompat.getInstance(this);
-
-            UserHandleCompat user = null;
-            if (intent.hasExtra(AppInfo.EXTRA_PROFILE)) {
-                long serialNumber = intent.getLongExtra(AppInfo.EXTRA_PROFILE, -1);
-                user = userManager.getUserForSerialNumber(serialNumber);
-            }
-
-            Bundle optsBundle = null;
-            if (useLaunchAnimation) {
-                ActivityOptions opts = null;
-                if (Utilities.ATLEAST_MARSHMALLOW) {
-                    int left = 0, top = 0;
-                    int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
-                    if (v instanceof TextView) {
-                        // Launch from center of icon, not entire view
-                        Drawable icon = Workspace.getTextViewIcon((TextView) v);
-                        if (icon != null) {
-                            Rect bounds = icon.getBounds();
-                            left = (width - bounds.width()) / 2;
-                            top = v.getPaddingTop();
-                            width = bounds.width();
-                            height = bounds.height();
-                        }
-                    }
-                    opts = ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
-                } else if (!Utilities.ATLEAST_LOLLIPOP) {
-                    // Below L, we use a scale up animation
-                    opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
-                                    v.getMeasuredWidth(), v.getMeasuredHeight());
-                } else if (Utilities.ATLEAST_LOLLIPOP_MR1) {
-                    // On L devices, we use the device default slide-up transition.
-                    // On L MR1 devices, we a custom version of the slide-up transition which
-                    // doesn't have the delay present in the device default.
-                    opts = ActivityOptions.makeCustomAnimation(this,
-                            R.anim.task_open_enter, R.anim.no_anim);
+                if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                    String id = ((ShortcutInfo) info).getDeepShortcutId();
+                    String packageName = intent.getPackage();
+                    LauncherAppState.getInstance().getShortcutManager().startShortcut(
+                            packageName, id, intent.getSourceBounds(), optsBundle, info.user);
+                } else {
+                    // Could be launching some bookkeeping activity
+                    startActivity(intent, optsBundle);
                 }
-                optsBundle = opts != null ? opts.toBundle() : null;
+            } finally {
+                StrictMode.setVmPolicy(oldPolicy);
             }
+        } catch (SecurityException e) {
+            // Due to legacy reasons, direct call shortcuts require Launchers to have the
+            // corresponding permission. Show the appropriate permission prompt if that
+            // is the case.
+            if (intent.getComponent() == null
+                    && Intent.ACTION_CALL.equals(intent.getAction())
+                    && checkSelfPermission(Manifest.permission.CALL_PHONE) !=
+                    PackageManager.PERMISSION_GRANTED) {
 
-            if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
-                // Could be launching some bookkeeping activity
-                startActivity(intent, optsBundle);
+                setWaitingForResult(PendingRequestArgs
+                        .forIntent(REQUEST_PERMISSION_CALL_PHONE, intent, info));
+                requestPermissions(new String[]{Manifest.permission.CALL_PHONE},
+                        REQUEST_PERMISSION_CALL_PHONE);
             } else {
-                // TODO Component can be null when shortcuts are supported for secondary user
-                launcherApps.startActivityForProfile(intent.getComponent(), user,
-                        intent.getSourceBounds(), optsBundle);
+                // No idea why this was thrown.
+                throw e;
             }
-            return true;
-        } catch (SecurityException e) {
-            if (Utilities.ATLEAST_MARSHMALLOW && tag instanceof ItemInfo) {
-                // Due to legacy reasons, direct call shortcuts require Launchers to have the
-                // corresponding permission. Show the appropriate permission prompt if that
-                // is the case.
-                if (intent.getComponent() == null
-                        && Intent.ACTION_CALL.equals(intent.getAction())
-                        && checkSelfPermission(Manifest.permission.CALL_PHONE) !=
-                            PackageManager.PERMISSION_GRANTED) {
-                    // TODO: Rename sPendingAddItem to a generic name.
-                    sPendingAddItem = preparePendingAddArgs(REQUEST_PERMISSION_CALL_PHONE, intent,
-                            0, (ItemInfo) tag);
-                    requestPermissions(new String[]{Manifest.permission.CALL_PHONE},
-                            REQUEST_PERMISSION_CALL_PHONE);
-                    return false;
-                }
-            }
-            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
-            Log.e(TAG, "Launcher does not have the permission to launch " + intent +
-                    ". Make sure to create a MAIN intent-filter for the corresponding activity " +
-                    "or use the exported attribute for this activity. "
-                    + "tag="+ tag + " intent=" + intent, e);
         }
-        return false;
     }
 
-    public boolean startActivitySafely(View v, Intent intent, Object tag) {
-        boolean success = false;
+    private Bundle getActivityLaunchOptions(View v) {
+        if (Utilities.ATLEAST_MARSHMALLOW) {
+            int left = 0, top = 0;
+            int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
+            if (v instanceof TextView) {
+                // Launch from center of icon, not entire view
+                Drawable icon = Workspace.getTextViewIcon((TextView) v);
+                if (icon != null) {
+                    Rect bounds = icon.getBounds();
+                    left = (width - bounds.width()) / 2;
+                    top = v.getPaddingTop();
+                    width = bounds.width();
+                    height = bounds.height();
+                }
+            }
+            return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height).toBundle();
+        } else if (Utilities.ATLEAST_LOLLIPOP_MR1) {
+            // On L devices, we use the device default slide-up transition.
+            // On L MR1 devices, we use a custom version of the slide-up transition which
+            // doesn't have the delay present in the device default.
+            return ActivityOptions.makeCustomAnimation(
+                    this, R.anim.task_open_enter, R.anim.no_anim).toBundle();
+        }
+        return null;
+    }
+
+    private Rect getViewBounds(View v) {
+        int[] pos = new int[2];
+        v.getLocationOnScreen(pos);
+        return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
+    }
+
+    public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
         if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
             Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
             return false;
         }
-        try {
-            success = startActivity(v, intent, tag);
-        } catch (ActivityNotFoundException e) {
-            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
-            Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e);
+        // Only launch using the new animation if the shortcut has not opted out (this is a
+        // private contract between launcher and may be ignored in the future).
+        boolean useLaunchAnimation = (v != null) &&
+                !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
+        Bundle optsBundle = useLaunchAnimation ? getActivityLaunchOptions(v) : null;
+
+        UserHandleCompat user = null;
+        if (intent.hasExtra(AppInfo.EXTRA_PROFILE)) {
+            long serialNumber = intent.getLongExtra(AppInfo.EXTRA_PROFILE, -1);
+            user = UserManagerCompat.getInstance(this).getUserForSerialNumber(serialNumber);
         }
-        return success;
+
+        // Prepare intent
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        if (v != null) {
+            intent.setSourceBounds(getViewBounds(v));
+        }
+        try {
+            if (Utilities.ATLEAST_MARSHMALLOW && item != null
+                    && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
+                    || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+                    && ((ShortcutInfo) item).promisedIntent == null) {
+                // Shortcuts need some special checks due to legacy reasons.
+                startShortcutIntentSafely(intent, optsBundle, item);
+            } else if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
+                // Could be launching some bookkeeping activity
+                startActivity(intent, optsBundle);
+            } else {
+                LauncherAppsCompat.getInstance(this).startActivityForProfile(
+                        intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
+            }
+            return true;
+        } catch (ActivityNotFoundException|SecurityException e) {
+            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+            Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
+        }
+        return false;
     }
 
     /**
@@ -3070,10 +2908,6 @@
 
     private void growAndFadeOutFolderIcon(FolderIcon fi) {
         if (fi == null) return;
-        PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0);
-        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.5f);
-        PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.5f);
-
         FolderInfo info = (FolderInfo) fi.getTag();
         if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
             CellLayout cl = (CellLayout) fi.getParent().getParent();
@@ -3085,8 +2919,8 @@
         copyFolderIconToImage(fi);
         fi.setVisibility(View.INVISIBLE);
 
-        ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
-                scaleX, scaleY);
+        ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(
+                mFolderIconImageView, 0, 1.5f, 1.5f);
         if (Utilities.ATLEAST_LOLLIPOP) {
             oa.setInterpolator(new LogDecelerateInterpolator(100, 0));
         }
@@ -3096,23 +2930,22 @@
 
     private void shrinkAndFadeInFolderIcon(final FolderIcon fi, boolean animate) {
         if (fi == null) return;
-        PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f);
-        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
-        PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f);
-
         final CellLayout cl = (CellLayout) fi.getParent().getParent();
 
         // We remove and re-draw the FolderIcon in-case it has changed
         mDragLayer.removeView(mFolderIconImageView);
         copyFolderIconToImage(fi);
-        ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
-                scaleX, scaleY);
+
+        if (cl != null) {
+            cl.clearFolderLeaveBehind();
+        }
+
+        ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(mFolderIconImageView, 1, 1, 1);
         oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
         oa.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 if (cl != null) {
-                    cl.clearFolderLeaveBehind();
                     // Remove the ImageView copy of the FolderIcon and make the original visible.
                     mDragLayer.removeView(mFolderIconImageView);
                     fi.setVisibility(View.VISIBLE);
@@ -3130,9 +2963,10 @@
      * is animated relative to the specified View. If the View is null, no animation
      * is played.
      *
-     * @param folderInfo The FolderInfo describing the folder to open.
+     * @param folderIcon The FolderIcon describing the folder to open.
      */
     public void openFolder(FolderIcon folderIcon) {
+
         Folder folder = folderIcon.getFolder();
         Folder openFolder = mWorkspace != null ? mWorkspace.getOpenFolder() : null;
         if (openFolder != null && openFolder != folder) {
@@ -3151,12 +2985,13 @@
         // There was a one-off crash where the folder had a parent already.
         if (folder.getParent() == null) {
             mDragLayer.addView(folder);
-            mDragController.addDropTarget((DropTarget) folder);
+            mDragController.addDropTarget(folder);
         } else {
             Log.w(TAG, "Opening folder (" + folder + ") which already has a parent (" +
                     folder.getParent() + ").");
         }
         folder.animateOpen();
+
         growAndFadeOutFolderIcon(folderIcon);
 
         // Notify the accessibility manager that this folder "window" has appeared and occluded
@@ -3180,6 +3015,8 @@
     }
 
     public void closeFolder(Folder folder, boolean animate) {
+        animate &= !Utilities.isPowerSaverOn(this);
+
         folder.getInfo().opened = false;
 
         ViewGroup parent = (ViewGroup) folder.getParent().getParent();
@@ -3201,12 +3038,53 @@
         getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
     }
 
+    public void closeShortcutsContainer() {
+        closeShortcutsContainer(true);
+    }
+
+    public void closeShortcutsContainer(boolean animate) {
+        DeepShortcutsContainer deepShortcutsContainer = getOpenShortcutsContainer();
+        if (deepShortcutsContainer != null) {
+            if (animate) {
+                deepShortcutsContainer.animateClose();
+            } else {
+                deepShortcutsContainer.close();
+            }
+        }
+    }
+
+    public View getTopFloatingView() {
+        View topView = getOpenShortcutsContainer();
+        if (topView == null) {
+            topView = getWorkspace().getOpenFolder();
+        }
+        return topView;
+    }
+
+    /**
+     * @return The open shortcuts container, or null if there is none
+     */
+    public DeepShortcutsContainer getOpenShortcutsContainer() {
+        // Iterate in reverse order. Shortcuts container is added later to the dragLayer,
+        // and will be one of the last views.
+        for (int i = mDragLayer.getChildCount() - 1; i >= 0; i--) {
+            View child = mDragLayer.getChildAt(i);
+            if (child instanceof DeepShortcutsContainer
+                    && ((DeepShortcutsContainer) child).isOpen()) {
+                return (DeepShortcutsContainer) child;
+            }
+        }
+        return null;
+    }
+
+    @Override
     public boolean onLongClick(View v) {
         if (!isDraggingEnabled()) return false;
         if (isWorkspaceLocked()) return false;
         if (mState != State.WORKSPACE) return false;
 
-        if (v == mAllAppsButton) {
+        if ((FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && v instanceof PageIndicator) ||
+                (v == mAllAppsButton && mAllAppsButton != null)) {
             onLongClickAllAppsButton(v);
             return true;
         }
@@ -3232,12 +3110,11 @@
             ItemInfo info = (ItemInfo) v.getTag();
             longClickCellInfo = new CellLayout.CellInfo(v, info);
             itemUnderLongClick = longClickCellInfo.cell;
-            resetAddInfo();
+            mPendingRequestArgs = null;
         }
 
         // The hotseat touch handling does not go through Workspace, and we always allow long press
         // on hotseat items.
-        final boolean inHotseat = isHotseatLayout(v);
         if (!mDragController.isDragging()) {
             if (itemUnderLongClick == null) {
                 // User long pressed on empty space
@@ -3249,13 +3126,23 @@
                     showOverviewMode(true);
                 }
             } else {
-                final boolean isAllAppsButton = inHotseat && isAllAppsButtonRank(
-                        mHotseat.getOrderInHotseat(
-                                longClickCellInfo.cellX,
-                                longClickCellInfo.cellY));
+                final boolean isAllAppsButton =
+                        !FeatureFlags.NO_ALL_APPS_ICON && isHotseatLayout(v) &&
+                                mDeviceProfile.inv.isAllAppsButtonRank(mHotseat.getOrderInHotseat(
+                                        longClickCellInfo.cellX, longClickCellInfo.cellY));
                 if (!(itemUnderLongClick instanceof Folder || isAllAppsButton)) {
                     // User long pressed on an item
-                    mWorkspace.startDrag(longClickCellInfo);
+                    DragOptions dragOptions = new DragOptions();
+                    if (itemUnderLongClick instanceof BubbleTextView) {
+                        BubbleTextView icon = (BubbleTextView) itemUnderLongClick;
+                        if (icon.hasDeepShortcuts()) {
+                            DeepShortcutsContainer dsc = DeepShortcutsContainer.showForIcon(icon);
+                            if (dsc != null) {
+                                dragOptions.deferDragCondition = dsc.createDeferDragCondition(null);
+                            }
+                        }
+                    }
+                    mWorkspace.startDrag(longClickCellInfo, dragOptions);
                 }
             }
         }
@@ -3297,29 +3184,6 @@
         return (mState == State.WIDGETS) || (mOnResumeState == State.WIDGETS);
     }
 
-    private void setWorkspaceBackground(int background) {
-        switch (background) {
-            case WORKSPACE_BACKGROUND_TRANSPARENT:
-                getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
-                break;
-            case WORKSPACE_BACKGROUND_BLACK:
-                getWindow().setBackgroundDrawable(null);
-                break;
-            default:
-                getWindow().setBackgroundDrawable(mWorkspaceBackgroundDrawable);
-        }
-    }
-
-    protected void changeWallpaperVisiblity(boolean visible) {
-        int wpflags = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
-        int curflags = getWindow().getAttributes().flags
-                & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-        if (wpflags != curflags) {
-            getWindow().setFlags(wpflags, WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER);
-        }
-        setWorkspaceBackground(visible ? WORKSPACE_BACKGROUND_GRADIENT : WORKSPACE_BACKGROUND_BLACK);
-    }
-
     @Override
     public void onTrimMemory(int level) {
         super.onTrimMemory(level);
@@ -3336,38 +3200,17 @@
         }
     }
 
-    /**
-     * @return whether or not the Launcher state changed.
-     */
     public boolean showWorkspace(boolean animated) {
-        return showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null);
+        return showWorkspace(animated, null);
     }
 
-    /**
-     * @return whether or not the Launcher state changed.
-     */
     public boolean showWorkspace(boolean animated, Runnable onCompleteRunnable) {
-        return showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated,
-                onCompleteRunnable);
-    }
-
-    /**
-     * @return whether or not the Launcher state changed.
-     */
-    protected boolean showWorkspace(int snapToPage, boolean animated) {
-        return showWorkspace(snapToPage, animated, null);
-    }
-
-    /**
-     * @return whether or not the Launcher state changed.
-     */
-    boolean showWorkspace(int snapToPage, boolean animated, Runnable onCompleteRunnable) {
         boolean changed = mState != State.WORKSPACE ||
                 mWorkspace.getState() != Workspace.State.NORMAL;
-        if (changed) {
+        if (changed || mAllAppsController.isTransitioning()) {
             mWorkspace.setVisibility(View.VISIBLE);
             mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
-                    Workspace.State.NORMAL, snapToPage, animated, onCompleteRunnable);
+                    Workspace.State.NORMAL, animated, onCompleteRunnable);
 
             // Set focus to the AppsCustomize button
             if (mAllAppsButton != null) {
@@ -3393,7 +3236,7 @@
     /**
      * Shows the overview button.
      */
-    void showOverviewMode(boolean animated) {
+    public void showOverviewMode(boolean animated) {
         showOverviewMode(animated, false);
     }
 
@@ -3416,20 +3259,19 @@
         }
         mWorkspace.setVisibility(View.VISIBLE);
         mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
-                Workspace.State.OVERVIEW,
-                WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated,
-                postAnimRunnable);
+                Workspace.State.OVERVIEW, animated, postAnimRunnable);
         mState = State.WORKSPACE;
+        // If animated from long press, then don't allow any of the controller in the drag
+        // layer to intercept any remaining touch.
+        mWorkspace.requestDisallowInterceptTouchEvent(animated);
     }
 
     /**
      * Shows the apps view.
      */
-    void showAppsView(boolean animated, boolean resetListToTop, boolean updatePredictedApps,
+    public void showAppsView(boolean animated, boolean updatePredictedApps,
             boolean focusSearchBar) {
-        if (resetListToTop) {
-            mAppsView.scrollToTop();
-        }
+        markAppsViewShown();
         if (updatePredictedApps) {
             tryAndUpdatePredictedApps();
         }
@@ -3462,14 +3304,22 @@
     // TODO: calling method should use the return value so that when {@code false} is returned
     // the workspace transition doesn't fall into invalid state.
     private boolean showAppsOrWidgets(State toState, boolean animated, boolean focusSearchBar) {
-        if (mState != State.WORKSPACE &&  mState != State.APPS_SPRING_LOADED &&
-                mState != State.WIDGETS_SPRING_LOADED) {
+        if (!(mState == State.WORKSPACE ||
+                mState == State.APPS_SPRING_LOADED ||
+                mState == State.WIDGETS_SPRING_LOADED ||
+                (mState == State.APPS && mAllAppsController.isTransitioning()))) {
             return false;
         }
         if (toState != State.APPS && toState != State.WIDGETS) {
             return false;
         }
 
+        // This is a safe and supported transition to bypass spring_loaded mode.
+        if (mExitSpringLoadedModeRunnable != null) {
+            mHandler.removeCallbacks(mExitSpringLoadedModeRunnable);
+            mExitSpringLoadedModeRunnable = null;
+        }
+
         if (toState == State.APPS) {
             mStateTransitionAnimation.startAnimationToAllApps(mWorkspace.getState(), animated,
                     focusSearchBar);
@@ -3484,6 +3334,7 @@
         mUserPresent = false;
         updateAutoAdvanceState();
         closeFolder();
+        closeShortcutsContainer();
 
         // Send an accessibility event to announce the context change
         getWindow().getDecorView()
@@ -3495,33 +3346,43 @@
      * Updates the workspace and interaction state on state change, and return the animation to this
      * new state.
      */
-    public Animator startWorkspaceStateChangeAnimation(Workspace.State toState, int toPage,
+    public Animator startWorkspaceStateChangeAnimation(Workspace.State toState,
             boolean animated, HashMap<View, Integer> layerViews) {
         Workspace.State fromState = mWorkspace.getState();
-        Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated, layerViews);
+        Animator anim = mWorkspace.setStateWithAnimation(toState, animated, layerViews);
         updateInteraction(fromState, toState);
         return anim;
     }
 
     public void enterSpringLoadedDragMode() {
         if (LOGD) Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", mState.name()));
-        if (mState == State.WORKSPACE || mState == State.APPS_SPRING_LOADED ||
-                mState == State.WIDGETS_SPRING_LOADED) {
+        if (isStateSpringLoaded()) {
             return;
         }
 
         mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
-                Workspace.State.SPRING_LOADED,
-                WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true /* animated */,
+                Workspace.State.SPRING_LOADED, true /* animated */,
                 null /* onCompleteRunnable */);
-        mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED;
+
+        if (isAppsViewVisible()) {
+            mState = State.APPS_SPRING_LOADED;
+        } else if (isWidgetsViewVisible()) {
+            mState = State.WIDGETS_SPRING_LOADED;
+        } else if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
+            mState = State.WORKSPACE_SPRING_LOADED;
+        } else {
+            mState = State.WORKSPACE;
+        }
     }
 
     public void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
             final Runnable onCompleteRunnable) {
-        if (mState != State.APPS_SPRING_LOADED && mState != State.WIDGETS_SPRING_LOADED) return;
+        if (!isStateSpringLoaded()) return;
 
-        mHandler.postDelayed(new Runnable() {
+        if (mExitSpringLoadedModeRunnable != null) {
+            mHandler.removeCallbacks(mExitSpringLoadedModeRunnable);
+        }
+        mExitSpringLoadedModeRunnable = new Runnable() {
             @Override
             public void run() {
                 if (successfulDrop) {
@@ -3535,16 +3396,25 @@
                 } else {
                     exitSpringLoadedDragMode();
                 }
+                mExitSpringLoadedModeRunnable = null;
             }
-        }, delay);
+        };
+        mHandler.postDelayed(mExitSpringLoadedModeRunnable, delay);
+    }
+
+    boolean isStateSpringLoaded() {
+        return mState == State.WORKSPACE_SPRING_LOADED || mState == State.APPS_SPRING_LOADED
+                || mState == State.WIDGETS_SPRING_LOADED;
     }
 
     void exitSpringLoadedDragMode() {
         if (mState == State.APPS_SPRING_LOADED) {
-            showAppsView(true /* animated */, false /* resetListToTop */,
+            showAppsView(true /* animated */,
                     false /* updatePredictedApps */, false /* focusSearchBar */);
         } else if (mState == State.WIDGETS_SPRING_LOADED) {
             showWidgetsView(true, false);
+        } else if (mState == State.WORKSPACE_SPRING_LOADED) {
+            showWorkspace(true);
         }
     }
 
@@ -3552,11 +3422,12 @@
      * Updates the set of predicted apps if it hasn't been updated since the last time Launcher was
      * resumed.
      */
-    private void tryAndUpdatePredictedApps() {
+    public void tryAndUpdatePredictedApps() {
         if (mLauncherCallbacks != null) {
             List<ComponentKey> apps = mLauncherCallbacks.getPredictedApps();
             if (apps != null) {
                 mAppsView.setPredictedApps(apps);
+                getUserEventDispatcher().setPredictedApps(apps);
             }
         }
     }
@@ -3569,104 +3440,6 @@
         // TODO
     }
 
-    public boolean launcherCallbacksProvidesSearch() {
-        return (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch());
-    }
-
-    public View getOrCreateQsbBar() {
-        if (launcherCallbacksProvidesSearch()) {
-            return mLauncherCallbacks.getQsbBar();
-        }
-
-        if (mQsb == null) {
-            AppWidgetProviderInfo searchProvider = Utilities.getSearchWidgetProvider(this);
-            if (searchProvider == null) {
-                return null;
-            }
-
-            Bundle opts = new Bundle();
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
-                    AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
-
-            // Determine the min and max dimensions of the widget.
-            LauncherAppState app = LauncherAppState.getInstance();
-            DeviceProfile portraitProfile = app.getInvariantDeviceProfile().portraitProfile;
-            DeviceProfile landscapeProfile = app.getInvariantDeviceProfile().landscapeProfile;
-            float density = getResources().getDisplayMetrics().density;
-            Point searchDimens = portraitProfile.getSearchBarDimensForWidgetOpts(getResources());
-            int maxHeight = (int) (searchDimens.y / density);
-            int minHeight = maxHeight;
-            int maxWidth = (int) (searchDimens.x / density);
-            int minWidth = maxWidth;
-            if (!landscapeProfile.isVerticalBarLayout()) {
-                searchDimens = landscapeProfile.getSearchBarDimensForWidgetOpts(getResources());
-                maxHeight = (int) Math.max(maxHeight, searchDimens.y / density);
-                minHeight = (int) Math.min(minHeight, searchDimens.y / density);
-                maxWidth = (int) Math.max(maxWidth, searchDimens.x / density);
-                minWidth = (int) Math.min(minWidth, searchDimens.x / density);
-            }
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, maxHeight);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, minHeight);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, maxWidth);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, minWidth);
-            if (LOGD) {
-                Log.d(TAG, "QSB widget options: maxHeight=" + maxHeight + " minHeight=" + minHeight
-                        + " maxWidth=" + maxWidth + " minWidth=" + minWidth);
-            }
-
-            if (mLauncherCallbacks != null) {
-                opts.putAll(mLauncherCallbacks.getAdditionalSearchWidgetOptions());
-            }
-
-            int widgetId = mSharedPrefs.getInt(QSB_WIDGET_ID, -1);
-            AppWidgetProviderInfo widgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
-            if (!searchProvider.provider.flattenToString().equals(
-                    mSharedPrefs.getString(QSB_WIDGET_PROVIDER, null))
-                    || (widgetInfo == null)
-                    || !widgetInfo.provider.equals(searchProvider.provider)) {
-                // A valid widget is not already bound.
-                if (widgetId > -1) {
-                    mAppWidgetHost.deleteAppWidgetId(widgetId);
-                    widgetId = -1;
-                }
-
-                // Try to bind a new widget
-                widgetId = mAppWidgetHost.allocateAppWidgetId();
-
-                if (!AppWidgetManagerCompat.getInstance(this)
-                        .bindAppWidgetIdIfAllowed(widgetId, searchProvider, opts)) {
-                    mAppWidgetHost.deleteAppWidgetId(widgetId);
-                    widgetId = -1;
-                }
-
-                mSharedPrefs.edit()
-                    .putInt(QSB_WIDGET_ID, widgetId)
-                    .putString(QSB_WIDGET_PROVIDER, searchProvider.provider.flattenToString())
-                    .apply();
-            }
-
-            mAppWidgetHost.setQsbWidgetId(widgetId);
-            if (widgetId != -1) {
-                mQsb = mAppWidgetHost.createView(this, widgetId, searchProvider);
-                mQsb.setId(R.id.qsb_widget);
-                mQsb.updateAppWidgetOptions(opts);
-                mQsb.setPadding(0, 0, 0, 0);
-                mSearchDropTargetBar.addView(mQsb);
-                mSearchDropTargetBar.setQsbSearchBar(mQsb);
-            }
-        }
-        return mQsb;
-    }
-
-    private void reinflateQSBIfNecessary() {
-        if (mQsb instanceof LauncherAppWidgetHostView &&
-                ((LauncherAppWidgetHostView) mQsb).isReinflateRequired()) {
-            mSearchDropTargetBar.removeView(mQsb);
-            mQsb = null;
-            mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());
-        }
-    }
-
     @Override
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
         final boolean result = super.dispatchPopulateAccessibilityEvent(event);
@@ -3686,16 +3459,6 @@
     }
 
     /**
-     * Receives notifications when system dialogs are to be closed.
-     */
-    @Thunk class CloseSystemDialogsIntentReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            closeSystemDialogs();
-        }
-    }
-
-    /**
      * If the activity is currently paused, signal that we need to run the passed Runnable
      * in onResume.
      *
@@ -3744,6 +3507,7 @@
      * @return true if we are currently paused.  The caller might be able to
      * skip some work in that case since we will come back again.
      */
+    @Override
     public boolean setLoadOnResume() {
         if (mPaused) {
             if (LOGD) Log.d(TAG, "setLoadOnResume");
@@ -3757,11 +3521,25 @@
     /**
      * Implementation of the method from LauncherModel.Callbacks.
      */
+    @Override
     public int getCurrentWorkspaceScreen() {
         if (mWorkspace != null) {
             return mWorkspace.getCurrentPage();
         } else {
-            return SCREEN_COUNT / 2;
+            return 0;
+        }
+    }
+
+    /**
+     * Clear any pending bind callbacks. This is called when is loader is planning to
+     * perform a full rebind from scratch.
+     */
+    @Override
+    public void clearPendingBinds() {
+        mBindOnResumeCallbacks.clear();
+        if (mPendingExecutor != null) {
+            mPendingExecutor.markCompleted();
+            mPendingExecutor = null;
         }
     }
 
@@ -3771,13 +3549,11 @@
      * Implementation of the method from LauncherModel.Callbacks.
      */
     public void startBinding() {
+        if (LauncherAppState.PROFILE_STARTUP) {
+            Trace.beginSection("Starting page bind");
+        }
         setWorkspaceLoading(true);
 
-        // If we're starting binding all over again, clear any bind calls we'd postponed in
-        // the past (see waitUntilResume) -- we don't need them since we're starting binding
-        // from scratch again
-        mBindOnResumeCallbacks.clear();
-
         // Clear the workspace because it's going to be rebound
         mWorkspace.clearDropTargets();
         mWorkspace.removeAllWorkspaceScreens();
@@ -3786,16 +3562,24 @@
         if (mHotseat != null) {
             mHotseat.resetLayout();
         }
+        if (LauncherAppState.PROFILE_STARTUP) {
+            Trace.endSection();
+        }
     }
 
     @Override
     public void bindScreens(ArrayList<Long> orderedScreenIds) {
-        bindAddScreens(orderedScreenIds);
-
-        // If there are no screens, we need to have an empty screen
-        if (orderedScreenIds.size() == 0) {
+        // Make sure the first screen is always at the start.
+        if (FeatureFlags.QSB_ON_FIRST_SCREEN &&
+                orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
+            orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID);
+            orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
+            mModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
+        } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) {
+            // If there are no screens, we need to have an empty screen
             mWorkspace.addExtraEmptyScreen();
         }
+        bindAddScreens(orderedScreenIds);
 
         // Create the custom content page (this call updates mDefaultScreen which calls
         // setCurrentPage() so ensure that all pages are added before calling this).
@@ -3803,13 +3587,21 @@
             mWorkspace.createCustomContentContainer();
             populateCustomContentContainer();
         }
+
+        // After we have added all the screens, if the wallpaper was locked to the default state,
+        // then notify to indicate that it can be released and a proper wallpaper offset can be
+        // computed before the next layout
+        mWorkspace.unlockWallpaperFromDefaultPageOnNextLayout();
     }
 
-    @Override
-    public void bindAddScreens(ArrayList<Long> orderedScreenIds) {
+    private void bindAddScreens(ArrayList<Long> orderedScreenIds) {
         int count = orderedScreenIds.size();
         for (int i = 0; i < count; i++) {
-            mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(orderedScreenIds.get(i));
+            long screenId = orderedScreenIds.get(i);
+            if (!FeatureFlags.QSB_ON_FIRST_SCREEN || screenId != Workspace.FIRST_SCREEN_ID) {
+                // No need to bind the first screen, as its always bound.
+                mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId);
+            }
         }
     }
 
@@ -3886,26 +3678,9 @@
             switch (item.itemType) {
                 case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                 case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                     ShortcutInfo info = (ShortcutInfo) item;
                     view = createShortcut(info);
-
-                    /*
-                     * TODO: FIX collision case
-                     */
-                    if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                        CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
-                        if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
-                            View v = cl.getChildAt(item.cellX, item.cellY);
-                            Object tag = v.getTag();
-                            String desc = "Collision while binding workspace item: " + item
-                                    + ". Collides with " + tag;
-                            if (LauncherAppState.isDogfoodBuild()) {
-                                throw (new RuntimeException(desc));
-                            } else {
-                                Log.d(TAG, desc);
-                            }
-                        }
-                    }
                     break;
                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                     view = FolderIcon.fromXml(R.layout.folder_icon, this,
@@ -3916,6 +3691,25 @@
                     throw new RuntimeException("Invalid Item Type");
             }
 
+             /*
+             * Remove colliding items.
+             */
+            if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+                CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
+                if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
+                    View v = cl.getChildAt(item.cellX, item.cellY);
+                    Object tag = v.getTag();
+                    String desc = "Collision while binding workspace item: " + item
+                            + ". Collides with " + tag;
+                    if (ProviderConfig.IS_DOGFOOD_BUILD) {
+                        throw (new RuntimeException(desc));
+                    } else {
+                        Log.d(TAG, desc);
+                        LauncherModel.deleteItemFromDatabase(this, item);
+                        continue;
+                    }
+                }
+            }
             workspace.addInScreenFromBind(view, item.container, item.screenId, item.cellX,
                     item.cellY, 1, 1);
             if (animateIcons) {
@@ -3959,28 +3753,12 @@
         workspace.requestLayout();
     }
 
-    /**
-     * Implementation of the method from LauncherModel.Callbacks.
-     */
-    public void bindFolders(final LongArrayMap<FolderInfo> folders) {
-        Runnable r = new Runnable() {
-            public void run() {
-                bindFolders(folders);
-            }
-        };
-        if (waitUntilResume(r)) {
-            return;
-        }
-        sFolders = folders.clone();
-    }
-
     private void bindSafeModeWidget(LauncherAppWidgetInfo item) {
         PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item, true);
         view.updateIcon(mIconCache);
-        item.hostView = view;
-        item.hostView.updateAppWidget(null);
-        item.hostView.setOnClickListener(this);
-        addAppWidgetToWorkspace(item, null, false);
+        view.updateAppWidget(null);
+        view.setOnClickListener(this);
+        addAppWidgetToWorkspace(view, item, null, false);
         mWorkspace.requestLayout();
     }
 
@@ -4036,41 +3814,49 @@
 
             // If we do not have a valid id, try to bind an id.
             if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
-                // Note: This assumes that the id remap broadcast is received before this step.
-                // If that is not the case, the id remap will be ignored and user may see the
-                // click to setup view.
-                PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(this, appWidgetInfo, null);
-                pendingInfo.spanX = item.spanX;
-                pendingInfo.spanY = item.spanY;
-                pendingInfo.minSpanX = item.minSpanX;
-                pendingInfo.minSpanY = item.minSpanY;
-                Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
+                if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
+                    // Id has not been allocated yet. Allocate a new id.
+                    item.appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+                    item.restoreStatus |= LauncherAppWidgetInfo.FLAG_ID_ALLOCATED;
 
-                int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
-                boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
-                        newWidgetId, appWidgetInfo, options);
+                    // Also try to bind the widget. If the bind fails, the user will be shown
+                    // a click to setup UI, which will ask for the bind permission.
+                    PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(this, appWidgetInfo);
+                    pendingInfo.spanX = item.spanX;
+                    pendingInfo.spanY = item.spanY;
+                    pendingInfo.minSpanX = item.minSpanX;
+                    pendingInfo.minSpanY = item.minSpanY;
+                    Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
 
-                // TODO consider showing a permission dialog when the widget is clicked.
-                if (!success) {
-                    mAppWidgetHost.deleteAppWidgetId(newWidgetId);
-                    if (DEBUG_WIDGETS) {
-                        Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
-                                + " belongs to component " + item.providerName
-                                + ", as the launcher is unable to bing a new widget id");
+                    boolean isDirectConfig =
+                            item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
+                    if (isDirectConfig && item.bindOptions != null) {
+                        Bundle newOptions = item.bindOptions.getExtras();
+                        if (options != null) {
+                            newOptions.putAll(options);
+                        }
+                        options = newOptions;
                     }
-                    LauncherModel.deleteItemFromDatabase(this, item);
-                    return;
+                    boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
+                            item.appWidgetId, appWidgetInfo, options);
+
+                    // We tried to bind once. If we were not able to bind, we would need to
+                    // go through the permission dialog, which means we cannot skip the config
+                    // activity.
+                    item.bindOptions = null;
+                    item.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG;
+
+                    // Bind succeeded
+                    if (success) {
+                        // If the widget has a configure activity, it is still needs to set it up,
+                        // otherwise the widget is ready to go.
+                        item.restoreStatus = (appWidgetInfo.configure == null) || isDirectConfig
+                                ? LauncherAppWidgetInfo.RESTORE_COMPLETED
+                                : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+                    }
+
+                    LauncherModel.updateItemInDatabase(this, item);
                 }
-
-                item.appWidgetId = newWidgetId;
-
-                // If the widget has a configure activity, it is still needs to set it up, otherwise
-                // the widget is ready to go.
-                item.restoreStatus = (appWidgetInfo.configure == null)
-                        ? LauncherAppWidgetInfo.RESTORE_COMPLETED
-                        : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
-
-                LauncherModel.updateItemInDatabase(this, item);
             } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
                     && (appWidgetInfo.configure == null)) {
                 // The widget was marked as UI not ready, but there is no configure activity to
@@ -4088,23 +3874,22 @@
 
             // Verify that we own the widget
             if (appWidgetInfo == null) {
-                Log.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
+                FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
                 deleteWidgetInfo(item);
                 return;
             }
 
-            item.hostView = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
             item.minSpanX = appWidgetInfo.minSpanX;
             item.minSpanY = appWidgetInfo.minSpanY;
-            addAppWidgetToWorkspace(item, appWidgetInfo, false);
+            addAppWidgetToWorkspace(
+                    mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo),
+                    item, appWidgetInfo, false);
         } else {
-            PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item,
-                    mIsSafeModeEnabled);
+            PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item, false);
             view.updateIcon(mIconCache);
-            item.hostView = view;
-            item.hostView.updateAppWidget(null);
-            item.hostView.setOnClickListener(this);
-            addAppWidgetToWorkspace(item, null, false);
+            view.updateAppWidget(null);
+            view.setOnClickListener(this);
+            addAppWidgetToWorkspace(view, item, null, false);
         }
         mWorkspace.requestLayout();
 
@@ -4118,26 +3903,67 @@
      * Restores a pending widget.
      *
      * @param appWidgetId The app widget id
-     * @param cellInfo The position on screen where to create the widget.
      */
-    private void completeRestoreAppWidget(final int appWidgetId) {
+    private LauncherAppWidgetInfo completeRestoreAppWidget(int appWidgetId, int finalRestoreFlag) {
         LauncherAppWidgetHostView view = mWorkspace.getWidgetForAppWidgetId(appWidgetId);
         if ((view == null) || !(view instanceof PendingAppWidgetHostView)) {
             Log.e(TAG, "Widget update called, when the widget no longer exists.");
-            return;
+            return null;
         }
 
         LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag();
-        info.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
+        info.restoreStatus = finalRestoreFlag;
 
         mWorkspace.reinflateWidgetsIfNecessary();
         LauncherModel.updateItemInDatabase(this, info);
+        return info;
     }
 
     public void onPageBoundSynchronously(int page) {
         mSynchronouslyBoundPages.add(page);
     }
 
+    @Override
+    public void executeOnNextDraw(ViewOnDrawExecutor executor) {
+        if (mPendingExecutor != null) {
+            mPendingExecutor.markCompleted();
+        }
+        mPendingExecutor = executor;
+        executor.attachTo(this);
+    }
+
+    public void clearPendingExecutor(ViewOnDrawExecutor executor) {
+        if (mPendingExecutor == executor) {
+            mPendingExecutor = null;
+        }
+    }
+
+    @Override
+    public void finishFirstPageBind(final ViewOnDrawExecutor executor) {
+        Runnable r = new Runnable() {
+            public void run() {
+                finishFirstPageBind(executor);
+            }
+        };
+        if (waitUntilResume(r)) {
+            return;
+        }
+
+        Runnable onComplete = new Runnable() {
+            @Override
+            public void run() {
+                if (executor != null) {
+                    executor.onLoadAnimationCompleted();
+                }
+            }
+        };
+        if (mDragLayer.getAlpha() < 1) {
+            mDragLayer.animate().alpha(1).withEndAction(onComplete).start();
+        } else {
+            onComplete.run();
+        }
+    }
+
     /**
      * Callback saying that there aren't any more items to bind.
      *
@@ -4152,33 +3978,25 @@
         if (waitUntilResume(r)) {
             return;
         }
+        if (LauncherAppState.PROFILE_STARTUP) {
+            Trace.beginSection("Page bind completed");
+        }
         if (mSavedState != null) {
             if (!mWorkspace.hasFocus()) {
                 mWorkspace.getChildAt(mWorkspace.getCurrentPage()).requestFocus();
             }
+
             mSavedState = null;
         }
 
         mWorkspace.restoreInstanceStateForRemainingPages();
 
         setWorkspaceLoading(false);
-        sendLoadingCompleteBroadcastIfNecessary();
 
-        // If we received the result of any pending adds while the loader was running (e.g. the
-        // widget configuration forced an orientation change), process them now.
-        if (sPendingAddItem != null) {
-            final long screenId = completeAdd(sPendingAddItem);
-
-            // TODO: this moves the user to the page where the pending item was added. Ideally,
-            // the screen would be guaranteed to exist after bind, and the page would be set through
-            // the workspace restore process.
-            mWorkspace.post(new Runnable() {
-                @Override
-                public void run() {
-                    mWorkspace.snapToScreenId(screenId);
-                }
-            });
-            sPendingAddItem = null;
+        if (mPendingActivityResult != null) {
+            handleActivityResult(mPendingActivityResult.requestCode,
+                    mPendingActivityResult.resultCode, mPendingActivityResult.data);
+            mPendingActivityResult = null;
         }
 
         InstallShortcutReceiver.disableAndFlushInstallQueue(this);
@@ -4186,38 +4004,18 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.finishBindingItems(false);
         }
-    }
-
-    private void sendLoadingCompleteBroadcastIfNecessary() {
-        if (!mSharedPrefs.getBoolean(FIRST_LOAD_COMPLETE, false)) {
-            String permission =
-                    getResources().getString(R.string.receive_first_load_broadcast_permission);
-            Intent intent = new Intent(ACTION_FIRST_LOAD_COMPLETE);
-            sendBroadcast(intent, permission);
-            SharedPreferences.Editor editor = mSharedPrefs.edit();
-            editor.putBoolean(FIRST_LOAD_COMPLETE, true);
-            editor.apply();
+        if (LauncherAppState.PROFILE_STARTUP) {
+            Trace.endSection();
         }
     }
 
-    public boolean isAllAppsButtonRank(int rank) {
-        if (mHotseat != null) {
-            return mHotseat.isAllAppsButtonRank(rank);
-        }
-        return false;
-    }
-
     private boolean canRunNewAppsAnimation() {
         long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime();
-        return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000)
-                && (mClings == null || !mClings.isVisible());
+        return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
     }
 
     private ValueAnimator createNewAppBounceAnimation(View v, int i) {
-        ValueAnimator bounceAnim = LauncherAnimUtils.ofPropertyValuesHolder(v,
-                PropertyValuesHolder.ofFloat("alpha", 1f),
-                PropertyValuesHolder.ofFloat("scaleX", 1f),
-                PropertyValuesHolder.ofFloat("scaleY", 1f));
+        ValueAnimator bounceAnim = LauncherAnimUtils.ofViewAlphaAndScale(v, 1, 1, 1);
         bounceAnim.setDuration(InstallShortcutReceiver.NEW_SHORTCUT_BOUNCE_DURATION);
         bounceAnim.setStartDelay(i * InstallShortcutReceiver.NEW_SHORTCUT_STAGGER_DELAY);
         bounceAnim.setInterpolator(new OvershootInterpolator(BOUNCE_ANIMATION_TENSION));
@@ -4228,11 +4026,6 @@
         return mDeviceProfile.isVerticalBarLayout();
     }
 
-    /** Returns the search bar bounds in pixels. */
-    protected Rect getSearchBarBounds() {
-        return mDeviceProfile.getSearchBarBounds(Utilities.isRtl(getResources()));
-    }
-
     public int getSearchBarHeight() {
         if (mLauncherCallbacks != null) {
             return mLauncherCallbacks.getSearchBarHeight();
@@ -4240,17 +4033,6 @@
         return LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL;
     }
 
-    public void bindSearchProviderChanged() {
-        if (mSearchDropTargetBar == null) {
-            return;
-        }
-        if (mQsb != null) {
-            mSearchDropTargetBar.removeView(mQsb);
-            mQsb = null;
-        }
-        mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());
-    }
-
     /**
      * A runnable that we can dequeue and re-enqueue when all applications are bound (to prevent
      * multiple calls to bind the same list.)
@@ -4283,6 +4065,29 @@
     }
 
     /**
+     * Copies LauncherModel's map of activities to shortcut ids to Launcher's. This is necessary
+     * because LauncherModel's map is updated in the background, while Launcher runs on the UI.
+     */
+    @Override
+    public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMapCopy) {
+        mDeepShortcutMap = deepShortcutMapCopy;
+        if (LOGD) Log.d(TAG, "bindDeepShortcutMap: " + mDeepShortcutMap);
+    }
+
+    public List<String> getShortcutIdsForItem(ItemInfo info) {
+        if (!DeepShortcutManager.supportsShortcuts(info)) {
+            return Collections.EMPTY_LIST;
+        }
+        ComponentName component = info.getTargetComponent();
+        if (component == null) {
+            return Collections.EMPTY_LIST;
+        }
+
+        List<String> ids = mDeepShortcutMap.get(new ComponentKey(component, info.user));
+        return ids == null ? Collections.EMPTY_LIST : ids;
+    }
+
+    /**
      * A package was updated.
      *
      * Implementation of the method from LauncherModel.Callbacks.
@@ -4317,8 +4122,12 @@
 
     /**
      * Some shortcuts were updated in the background.
-     *
      * Implementation of the method from LauncherModel.Callbacks.
+     *
+     * @param updated list of shortcuts which have changed.
+     * @param removed list of shortcuts which were deleted in the background. This can happen when
+     *                an app gets removed from the system or some of its components are no longer
+     *                available.
      */
     @Override
     public void bindShortcutsChanged(final ArrayList<ShortcutInfo> updated,
@@ -4337,13 +4146,28 @@
         }
 
         if (!removed.isEmpty()) {
-            HashSet<ComponentName> removedComponents = new HashSet<ComponentName>();
+            HashSet<ComponentName> removedComponents = new HashSet<>();
+            HashSet<ShortcutKey> removedDeepShortcuts = new HashSet<>();
+
             for (ShortcutInfo si : removed) {
-                removedComponents.add(si.getTargetComponent());
+                if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                    removedDeepShortcuts.add(ShortcutKey.fromShortcutInfo(si));
+                } else {
+                    removedComponents.add(si.getTargetComponent());
+                }
             }
-            mWorkspace.removeItemsByComponentName(removedComponents, user);
-            // Notify the drag controller
-            mDragController.onAppsRemoved(new HashSet<String>(), removedComponents);
+
+            if (!removedComponents.isEmpty()) {
+                ItemInfoMatcher matcher = ItemInfoMatcher.ofComponents(removedComponents, user);
+                mWorkspace.removeItemsByMatcher(matcher);
+                mDragController.onAppsRemoved(matcher);
+            }
+
+            if (!removedDeepShortcuts.isEmpty()) {
+                ItemInfoMatcher matcher = ItemInfoMatcher.ofShortcutKeys(removedDeepShortcuts);
+                mWorkspace.removeItemsByMatcher(matcher);
+                mDragController.onAppsRemoved(matcher);
+            }
         }
     }
 
@@ -4386,14 +4210,16 @@
             return;
         }
         if (!packageNames.isEmpty()) {
-            mWorkspace.removeItemsByPackageName(packageNames, user);
+            ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageNames, user);
+            mWorkspace.removeItemsByMatcher(matcher);
+            mDragController.onAppsRemoved(matcher);
+
         }
         if (!components.isEmpty()) {
-            mWorkspace.removeItemsByComponentName(components, user);
+            ItemInfoMatcher matcher = ItemInfoMatcher.ofComponents(components, user);
+            mWorkspace.removeItemsByMatcher(matcher);
+            mDragController.onAppsRemoved(matcher);
         }
-        // Notify the drag controller
-        mDragController.onAppsRemoved(packageNames, components);
-
     }
 
     @Override
@@ -4492,183 +4318,34 @@
                     public void run() {
                         setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
                     }
-                }, mRestoreScreenOrientationDelay);
+                }, RESTORE_SCREEN_ORIENTATION_DELAY);
             }
         }
     }
 
-    protected boolean isLauncherPreinstalled() {
-        if (mLauncherCallbacks != null) {
-            return mLauncherCallbacks.isLauncherPreinstalled();
+    private void markAppsViewShown() {
+        if (mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false)) {
+            return;
         }
-        PackageManager pm = getPackageManager();
-        try {
-            ApplicationInfo ai = pm.getApplicationInfo(getComponentName().getPackageName(), 0);
-            if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                return true;
-            } else {
-                return false;
-            }
-        } catch (NameNotFoundException e) {
-            e.printStackTrace();
+        mSharedPrefs.edit().putBoolean(APPS_VIEW_SHOWN, true).apply();
+    }
+
+    private boolean shouldShowDiscoveryBounce() {
+        if (mState != mState.WORKSPACE) {
             return false;
         }
-    }
-
-    /**
-     * This method indicates whether or not we should suggest default wallpaper dimensions
-     * when our wallpaper cropper was not yet used to set a wallpaper.
-     */
-    protected boolean overrideWallpaperDimensions() {
-        if (mLauncherCallbacks != null) {
-            return mLauncherCallbacks.overrideWallpaperDimensions();
+        if (mLauncherCallbacks != null && mLauncherCallbacks.shouldShowDiscoveryBounce()) {
+            return true;
+        }
+        if (!mIsResumeFromActionScreenOff) {
+            return false;
+        }
+        if (mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false)) {
+            return false;
         }
         return true;
     }
 
-    /**
-     * To be overridden by subclasses to indicate that there is an activity to launch
-     * before showing the standard launcher experience.
-     */
-    protected boolean hasFirstRunActivity() {
-        if (mLauncherCallbacks != null) {
-            return mLauncherCallbacks.hasFirstRunActivity();
-        }
-        return false;
-    }
-
-    /**
-     * To be overridden by subclasses to launch any first run activity
-     */
-    protected Intent getFirstRunActivity() {
-        if (mLauncherCallbacks != null) {
-            return mLauncherCallbacks.getFirstRunActivity();
-        }
-        return null;
-    }
-
-    private boolean shouldRunFirstRunActivity() {
-        return !ActivityManager.isRunningInTestHarness() &&
-                !mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false);
-    }
-
-    protected boolean hasRunFirstRunActivity() {
-        return mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false);
-    }
-
-    public boolean showFirstRunActivity() {
-        if (shouldRunFirstRunActivity() &&
-                hasFirstRunActivity()) {
-            Intent firstRunIntent = getFirstRunActivity();
-            if (firstRunIntent != null) {
-                startActivity(firstRunIntent);
-                markFirstRunActivityShown();
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private void markFirstRunActivityShown() {
-        SharedPreferences.Editor editor = mSharedPrefs.edit();
-        editor.putBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, true);
-        editor.apply();
-    }
-
-    /**
-     * To be overridden by subclasses to indicate that there is an in-activity full-screen intro
-     * screen that must be displayed and dismissed.
-     */
-    protected boolean hasDismissableIntroScreen() {
-        if (mLauncherCallbacks != null) {
-            return mLauncherCallbacks.hasDismissableIntroScreen();
-        }
-        return false;
-    }
-
-    /**
-     * Full screen intro screen to be shown and dismissed before the launcher can be used.
-     */
-    protected View getIntroScreen() {
-        if (mLauncherCallbacks != null) {
-            return mLauncherCallbacks.getIntroScreen();
-        }
-        return null;
-    }
-
-    /**
-     * To be overriden by subclasses to indicate whether the in-activity intro screen has been
-     * dismissed. This method is ignored if #hasDismissableIntroScreen returns false.
-     */
-    private boolean shouldShowIntroScreen() {
-        return hasDismissableIntroScreen() &&
-                !mSharedPrefs.getBoolean(INTRO_SCREEN_DISMISSED, false);
-    }
-
-    protected void showIntroScreen() {
-        View introScreen = getIntroScreen();
-        changeWallpaperVisiblity(false);
-        if (introScreen != null) {
-            mDragLayer.showOverlayView(introScreen);
-        }
-    }
-
-    public void dismissIntroScreen() {
-        markIntroScreenDismissed();
-        if (showFirstRunActivity()) {
-            // We delay hiding the intro view until the first run activity is showing. This
-            // avoids a blip.
-            mWorkspace.postDelayed(new Runnable() {
-                @Override
-                public void run() {
-                    mDragLayer.dismissOverlayView();
-                    showFirstRunClings();
-                }
-            }, ACTIVITY_START_DELAY);
-        } else {
-            mDragLayer.dismissOverlayView();
-            showFirstRunClings();
-        }
-        changeWallpaperVisiblity(true);
-    }
-
-    private void markIntroScreenDismissed() {
-        SharedPreferences.Editor editor = mSharedPrefs.edit();
-        editor.putBoolean(INTRO_SCREEN_DISMISSED, true);
-        editor.apply();
-    }
-
-    @Thunk void showFirstRunClings() {
-        // The two first run cling paths are mutually exclusive, if the launcher is preinstalled
-        // on the device, then we always show the first run cling experience (or if there is no
-        // launcher2). Otherwise, we prompt the user upon started for migration
-        LauncherClings launcherClings = new LauncherClings(this);
-        if (launcherClings.shouldShowFirstRunOrMigrationClings()) {
-            mClings = launcherClings;
-            if (mModel.canMigrateFromOldLauncherDb(this)) {
-                launcherClings.showMigrationCling();
-            } else {
-                launcherClings.showLongPressCling(true);
-            }
-        }
-    }
-
-    void showWorkspaceSearchAndHotseat() {
-        if (mWorkspace != null) mWorkspace.setAlpha(1f);
-        if (mHotseat != null) mHotseat.setAlpha(1f);
-        if (mPageIndicators != null) mPageIndicators.setAlpha(1f);
-        if (mSearchDropTargetBar != null) mSearchDropTargetBar.animateToState(
-                SearchDropTargetBar.State.SEARCH_BAR, 0);
-    }
-
-    void hideWorkspaceSearchAndHotseat() {
-        if (mWorkspace != null) mWorkspace.setAlpha(0f);
-        if (mHotseat != null) mHotseat.setAlpha(0f);
-        if (mPageIndicators != null) mPageIndicators.setAlpha(0f);
-        if (mSearchDropTargetBar != null) mSearchDropTargetBar.animateToState(
-                SearchDropTargetBar.State.INVISIBLE, 0);
-    }
-
     // TODO: These method should be a part of LauncherSearchCallback
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     public ItemInfo createAppDragInfo(Intent appLaunchIntent) {
@@ -4706,24 +4383,10 @@
                 UserHandleCompat.myUserHandle());
     }
 
-    // TODO: This method should be a part of LauncherSearchCallback
-    public void startDrag(View dragView, ItemInfo dragInfo, DragSource source) {
-        dragView.setTag(dragInfo);
-        mWorkspace.onExternalDragStartedWithItem(dragView);
-        mWorkspace.beginExternalDragShared(dragView, source);
-    }
-
     protected void moveWorkspaceToDefaultScreen() {
         mWorkspace.moveToDefaultScreen(false);
     }
 
-    @Override
-    public void onPageSwitch(View newPage, int newPageIndex) {
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onPageSwitch(newPage, newPageIndex);
-        }
-    }
-
     /**
      * Returns a FastBitmapDrawable with the icon, accurately sized.
      */
@@ -4749,10 +4412,8 @@
         Log.d(TAG, "BEGIN launcher3 dump state for launcher " + this);
         Log.d(TAG, "mSavedState=" + mSavedState);
         Log.d(TAG, "mWorkspaceLoading=" + mWorkspaceLoading);
-        Log.d(TAG, "mRestoring=" + mRestoring);
-        Log.d(TAG, "mWaitingForResult=" + mWaitingForResult);
-        Log.d(TAG, "mSavedInstanceState=" + mSavedInstanceState);
-        Log.d(TAG, "sFolders.size=" + sFolders.size());
+        Log.d(TAG, "mPendingRequestArgs=" + mPendingRequestArgs);
+        Log.d(TAG, "mPendingActivityResult=" + mPendingActivityResult);
         mModel.dumpState();
         // TODO(hyunyoungs): add mWidgetsView.dumpState(); or mWidgetsModel.dumpState();
 
@@ -4762,54 +4423,40 @@
     @Override
     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
         super.dump(prefix, fd, writer, args);
-        synchronized (sDumpLogs) {
-            writer.println(" ");
-            writer.println("Debug logs: ");
-            for (int i = 0; i < sDumpLogs.size(); i++) {
-                writer.println("  " + sDumpLogs.get(i));
+        // Dump workspace
+        writer.println(prefix + "Workspace Items");
+        for (int i = mWorkspace.numCustomPages(); i < mWorkspace.getPageCount(); i++) {
+            writer.println(prefix + "  Homescreen " + i);
+
+            ViewGroup layout = ((CellLayout) mWorkspace.getPageAt(i)).getShortcutsAndWidgets();
+            for (int j = 0; j < layout.getChildCount(); j++) {
+                Object tag = layout.getChildAt(j).getTag();
+                if (tag != null) {
+                    writer.println(prefix + "    " + tag.toString());
+                }
             }
         }
+
+        writer.println(prefix + "  Hotseat");
+        ViewGroup layout = mHotseat.getLayout().getShortcutsAndWidgets();
+        for (int j = 0; j < layout.getChildCount(); j++) {
+            Object tag = layout.getChildAt(j).getTag();
+            if (tag != null) {
+                writer.println(prefix + "    " + tag.toString());
+            }
+        }
+
+        try {
+            FileLog.flushAll(writer);
+        } catch (Exception e) {
+            // Ignore
+        }
+
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.dump(prefix, fd, writer, args);
         }
     }
 
-    public static void dumpDebugLogsToConsole() {
-        if (DEBUG_DUMP_LOG) {
-            synchronized (sDumpLogs) {
-                Log.d(TAG, "");
-                Log.d(TAG, "*********************");
-                Log.d(TAG, "Launcher debug logs: ");
-                for (int i = 0; i < sDumpLogs.size(); i++) {
-                    Log.d(TAG, "  " + sDumpLogs.get(i));
-                }
-                Log.d(TAG, "*********************");
-                Log.d(TAG, "");
-            }
-        }
-    }
-
-    public static void addDumpLog(String tag, String log, boolean debugLog) {
-        addDumpLog(tag, log, null, debugLog);
-    }
-
-    public static void addDumpLog(String tag, String log, Exception e, boolean debugLog) {
-        if (debugLog) {
-            if (e != null) {
-                Log.d(tag, log, e);
-            } else {
-                Log.d(tag, log);
-            }
-        }
-        if (DEBUG_DUMP_LOG) {
-            sDateStamp.setTime(System.currentTimeMillis());
-            synchronized (sDumpLogs) {
-                sDumpLogs.add(sDateFormat.format(sDateStamp) + ": " + tag + ", " + log
-                    + (e == null ? "" : (", Exception: " + e)));
-            }
-        }
-    }
-
     public static CustomAppWidget getCustomAppWidget(String name) {
         return sCustomAppWidgets.get(name);
     }
@@ -4818,53 +4465,6 @@
         return sCustomAppWidgets;
     }
 
-    public void dumpLogsToLocalData() {
-        if (DEBUG_DUMP_LOG) {
-            new AsyncTask<Void, Void, Void>() {
-                public Void doInBackground(Void ... args) {
-                    boolean success = false;
-                    sDateStamp.setTime(sRunStart);
-                    String FILENAME = sDateStamp.getMonth() + "-"
-                            + sDateStamp.getDay() + "_"
-                            + sDateStamp.getHours() + "-"
-                            + sDateStamp.getMinutes() + "_"
-                            + sDateStamp.getSeconds() + ".txt";
-
-                    FileOutputStream fos = null;
-                    File outFile = null;
-                    try {
-                        outFile = new File(getFilesDir(), FILENAME);
-                        outFile.createNewFile();
-                        fos = new FileOutputStream(outFile);
-                    } catch (Exception e) {
-                        e.printStackTrace();
-                    }
-                    if (fos != null) {
-                        PrintWriter writer = new PrintWriter(fos);
-
-                        writer.println(" ");
-                        writer.println("Debug logs: ");
-                        synchronized (sDumpLogs) {
-                            for (int i = 0; i < sDumpLogs.size(); i++) {
-                                writer.println("  " + sDumpLogs.get(i));
-                            }
-                        }
-                        writer.close();
-                    }
-                    try {
-                        if (fos != null) {
-                            fos.close();
-                            success = true;
-                        }
-                    } catch (IOException e) {
-                        e.printStackTrace();
-                    }
-                    return null;
-                }
-            }.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
-        }
-    }
-
     public static List<View> getFolderContents(View icon) {
         if (icon instanceof FolderIcon) {
             return ((FolderIcon) icon).getFolder().getItemsInReadingOrder();
@@ -4872,9 +4472,30 @@
             return Collections.EMPTY_LIST;
         }
     }
-}
 
-interface DebugIntents {
-    static final String DELETE_DATABASE = "com.android.launcher3.action.DELETE_DATABASE";
-    static final String MIGRATE_DATABASE = "com.android.launcher3.action.MIGRATE_DATABASE";
+    public static Launcher getLauncher(Context context) {
+        if (context instanceof Launcher) {
+            return (Launcher) context;
+        }
+        return ((Launcher) ((ContextWrapper) context).getBaseContext());
+    }
+
+    private class RotationPrefChangeHandler implements OnSharedPreferenceChangeListener, Runnable {
+
+        @Override
+        public void onSharedPreferenceChanged(
+                SharedPreferences sharedPreferences, String key) {
+            if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(key)) {
+                mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext());
+                if (!waitUntilResume(this, true)) {
+                    run();
+                }
+            }
+        }
+
+        @Override
+        public void run() {
+            setOrientation();
+        }
+    }
 }
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index fa20f03..01e73d4 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -21,13 +21,10 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
-import android.annotation.TargetApi;
-import android.os.Build;
+import android.util.Property;
 import android.view.View;
 import android.view.ViewTreeObserver;
 
-import com.android.launcher3.util.UiThreadCircularReveal;
-
 import java.util.HashSet;
 import java.util.WeakHashMap;
 
@@ -102,42 +99,32 @@
         return anim;
     }
 
-    public static ObjectAnimator ofFloat(View target, String propertyName, float... values) {
-        ObjectAnimator anim = new ObjectAnimator();
-        anim.setTarget(target);
-        anim.setPropertyName(propertyName);
-        anim.setFloatValues(values);
+    public static ObjectAnimator ofFloat(View target, Property<View, Float> property,
+            float... values) {
+        ObjectAnimator anim = ObjectAnimator.ofFloat(target, property, values);
         cancelOnDestroyActivity(anim);
         new FirstFrameAnimatorHelper(anim, target);
         return anim;
     }
 
+    public static ObjectAnimator ofViewAlphaAndScale(View target,
+            float alpha, float scaleX, float scaleY) {
+        return ofPropertyValuesHolder(target,
+                PropertyValuesHolder.ofFloat(View.ALPHA, alpha),
+                PropertyValuesHolder.ofFloat(View.SCALE_X, scaleX),
+                PropertyValuesHolder.ofFloat(View.SCALE_Y, scaleY));
+    }
+
     public static ObjectAnimator ofPropertyValuesHolder(View target,
             PropertyValuesHolder... values) {
-        ObjectAnimator anim = new ObjectAnimator();
-        anim.setTarget(target);
-        anim.setValues(values);
-        cancelOnDestroyActivity(anim);
-        new FirstFrameAnimatorHelper(anim, target);
-        return anim;
+        return ofPropertyValuesHolder(target, target, values);
     }
 
     public static ObjectAnimator ofPropertyValuesHolder(Object target,
             View view, PropertyValuesHolder... values) {
-        ObjectAnimator anim = new ObjectAnimator();
-        anim.setTarget(target);
-        anim.setValues(values);
+        ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(target, values);
         cancelOnDestroyActivity(anim);
         new FirstFrameAnimatorHelper(anim, view);
         return anim;
     }
-
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-    public static ValueAnimator createCircularReveal(View view, int centerX,
-            int centerY, float startRadius, float endRadius) {
-        ValueAnimator anim = UiThreadCircularReveal.createCircularReveal(view, centerX,
-                centerY, startRadius, endRadius);
-        new FirstFrameAnimatorHelper(anim, view);
-        return anim;
-    }
 }
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index e1ade10..7861a10 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -16,17 +16,20 @@
 
 package com.android.launcher3;
 
-import android.app.SearchManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.util.Log;
 
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
 import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.dynamicui.ExtractionUtils;
+import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutCache;
 import com.android.launcher3.util.ConfigMonitor;
 import com.android.launcher3.util.TestingUtils;
 import com.android.launcher3.util.Thunk;
@@ -35,12 +38,15 @@
 
 public class LauncherAppState {
 
+    public static final boolean PROFILE_STARTUP = ProviderConfig.IS_DOGFOOD_BUILD;
+
     private final AppFilter mAppFilter;
     @Thunk final LauncherModel mModel;
     private final IconCache mIconCache;
     private final WidgetPreviewLoader mWidgetCache;
+    private final DeepShortcutManager mDeepShortcutManager;
 
-    private boolean mWallpaperChangedSinceLastCheck;
+    @Thunk boolean mWallpaperChangedSinceLastCheck;
 
     private static WeakReference<LauncherProvider> sLauncherProvider;
     private static Context sContext;
@@ -49,8 +55,6 @@
 
     private InvariantDeviceProfile mInvariantDeviceProfile;
 
-    private LauncherAccessibilityDelegate mAccessibilityDelegate;
-
     public static LauncherAppState getInstance() {
         if (INSTANCE == null) {
             INSTANCE = new LauncherAppState();
@@ -66,11 +70,18 @@
         return sContext;
     }
 
-    public static void setApplicationContext(Context context) {
-        if (sContext != null) {
-            Log.w(Launcher.TAG, "setApplicationContext called twice! old=" + sContext + " new=" + context);
+    static void setLauncherProvider(LauncherProvider provider) {
+        if (sLauncherProvider != null) {
+            Log.w(Launcher.TAG, "setLauncherProvider called twice! old=" +
+                    sLauncherProvider.get() + " new=" + provider);
         }
-        sContext = context.getApplicationContext();
+        sLauncherProvider = new WeakReference<>(provider);
+
+        // The content provider exists for the entire duration of the launcher main process and
+        // is the first component to get created. Initializing application context here ensures
+        // that LauncherAppState always exists in the main process.
+        sContext = provider.getContext().getApplicationContext();
+        FileLog.setDir(sContext.getFilesDir());
     }
 
     private LauncherAppState() {
@@ -87,28 +98,42 @@
         mInvariantDeviceProfile = new InvariantDeviceProfile(sContext);
         mIconCache = new IconCache(sContext, mInvariantDeviceProfile);
         mWidgetCache = new WidgetPreviewLoader(sContext, mIconCache);
+        mDeepShortcutManager = new DeepShortcutManager(sContext, new ShortcutCache());
 
         mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class));
-        mModel = new LauncherModel(this, mIconCache, mAppFilter);
+        mModel = new LauncherModel(this, mIconCache, mAppFilter, mDeepShortcutManager);
 
         LauncherAppsCompat.getInstance(sContext).addOnAppsChangedCallback(mModel);
 
         // Register intent receivers
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_LOCALE_CHANGED);
-        filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
         // For handling managed profiles
-        filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED);
-        filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED);
-        filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_AVAILABLE);
-        filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+        filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+        filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
+        filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
+        filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
+        filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
+        // For extracting colors from the wallpaper
+        if (Utilities.isNycOrAbove()) {
+            // TODO: add a broadcast entry to the manifest for pre-N.
+            filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
+        }
 
         sContext.registerReceiver(mModel, filter);
         UserManagerCompat.getInstance(sContext).enableAndResetCache();
+        if (!Utilities.ATLEAST_KITKAT) {
+            sContext.registerReceiver(new BroadcastReceiver() {
+
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    mWallpaperChangedSinceLastCheck = true;
+                }
+            }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
+        }
         new ConfigMonitor(sContext).register();
 
-        sContext.registerReceiver(
-                new WallpaperChangedReceiver(), new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
+        ExtractionUtils.startColorExtractionServiceIfNecessary(sContext);
     }
 
     /**
@@ -131,17 +156,11 @@
     }
 
     LauncherModel setLauncher(Launcher launcher) {
-        getLauncherProvider().setLauncherProviderChangeListener(launcher);
+        sLauncherProvider.get().setLauncherProviderChangeListener(launcher);
         mModel.initialize(launcher);
-        mAccessibilityDelegate = ((launcher != null) && Utilities.ATLEAST_LOLLIPOP) ?
-            new LauncherAccessibilityDelegate(launcher) : null;
         return mModel;
     }
 
-    public LauncherAccessibilityDelegate getAccessibilityDelegate() {
-        return mAccessibilityDelegate;
-    }
-
     public IconCache getIconCache() {
         return mIconCache;
     }
@@ -150,20 +169,12 @@
         return mModel;
     }
 
-    static void setLauncherProvider(LauncherProvider provider) {
-        sLauncherProvider = new WeakReference<LauncherProvider>(provider);
-    }
-
-    public static LauncherProvider getLauncherProvider() {
-        return sLauncherProvider.get();
-    }
-
     public WidgetPreviewLoader getWidgetCache() {
         return mWidgetCache;
     }
 
-    public void onWallpaperChanged() {
-        mWallpaperChangedSinceLastCheck = true;
+    public DeepShortcutManager getShortcutManager() {
+        return mDeepShortcutManager;
     }
 
     public boolean hasWallpaperChangedSinceLastCheck() {
@@ -175,8 +186,4 @@
     public InvariantDeviceProfile getInvariantDeviceProfile() {
         return mInvariantDeviceProfile;
     }
-
-    public static boolean isDogfoodBuild() {
-        return FeatureFlags.IS_ALPHA_BUILD || FeatureFlags.IS_DEV_BUILD;
-    }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 8c23ff3..d3e5350 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -37,7 +37,6 @@
 
     private final ArrayList<Runnable> mProviderChangeListeners = new ArrayList<Runnable>();
 
-    private int mQsbWidgetId = -1;
     private Launcher mLauncher;
 
     public LauncherAppWidgetHost(Launcher launcher, int hostId) {
@@ -45,23 +44,9 @@
         mLauncher = launcher;
     }
 
-    public void setQsbWidgetId(int widgetId) {
-        mQsbWidgetId = widgetId;
-    }
-
     @Override
     protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
             AppWidgetProviderInfo appWidget) {
-        if (appWidgetId == mQsbWidgetId) {
-            return new LauncherAppWidgetHostView(context) {
-
-                @Override
-                protected View getErrorView() {
-                    // For the QSB, show an empty view instead of an error view.
-                    return new View(getContext());
-                }
-            };
-        }
         return new LauncherAppWidgetHostView(context);
     }
 
@@ -82,12 +67,6 @@
         }
     }
 
-    @Override
-    public void stopListening() {
-        super.stopListening();
-        clearViews();
-    }
-
     public void addProviderChangeListener(Runnable callback) {
         mProviderChangeListeners.add(callback);
     }
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 0271089..ed1079f 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -25,10 +25,12 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.ViewDebug;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.RemoteViews;
 
-import com.android.launcher3.DragLayer.TouchCompleteListener;
+import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
 
 import java.util.ArrayList;
 
@@ -42,28 +44,29 @@
     private CheckLongPressHelper mLongPressHelper;
     private StylusEventHelper mStylusEventHelper;
     private Context mContext;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private int mPreviousOrientation;
-    private DragLayer mDragLayer;
 
     private float mSlop;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mChildrenFocused;
 
+    protected int mErrorViewId = R.layout.appwidget_error;
+
     public LauncherAppWidgetHostView(Context context) {
         super(context);
         mContext = context;
         mLongPressHelper = new CheckLongPressHelper(this);
-        mStylusEventHelper = new StylusEventHelper(this);
+        mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        mDragLayer = ((Launcher) context).getDragLayer();
-        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
-
+        setAccessibilityDelegate(Launcher.getLauncher(context).getAccessibilityDelegate());
         setBackgroundResource(R.drawable.widget_internal_focus_bg);
     }
 
     @Override
     protected View getErrorView() {
-        return mInflater.inflate(R.layout.appwidget_error, this, false);
+        return mInflater.inflate(mErrorViewId, this, false);
     }
 
     public void updateLastInflationOrientation() {
@@ -101,7 +104,7 @@
 
         // Watch for longpress or stylus button press events at this level to
         // make sure users can always pick up this widget
-        if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) {
+        if (mStylusEventHelper.onMotionEvent(ev)) {
             mLongPressHelper.cancelLongPress();
             return true;
         }
@@ -110,7 +113,7 @@
                 if (!mStylusEventHelper.inStylusButtonPressed()) {
                     mLongPressHelper.postCheckForLongPress();
                 }
-                mDragLayer.setTouchCompleteListener(this);
+                Launcher.getLauncher(getContext()).getDragLayer().setTouchCompleteListener(this);
                 break;
             }
 
@@ -287,4 +290,10 @@
             });
         }
     }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setClassName(getClass().getName());
+    }
 }
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 55edf45..66d8957 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.Intent;
 
 import com.android.launcher3.compat.UserHandleCompat;
 
@@ -51,6 +52,18 @@
     public static final int FLAG_RESTORE_STARTED = 8;
 
     /**
+     * Indicates that the widget has been allocated an Id. The id is still not valid, as it has
+     * not been bound yet.
+     */
+    public static final int FLAG_ID_ALLOCATED = 16;
+
+    /**
+     * Indicates that the widget does not need to show config activity, even if it has a
+     * configuration screen. It can also optionally have some extras which are sent during bind.
+     */
+    public static final int FLAG_DIRECT_CONFIG = 32;
+
+    /**
      * Indicates that the widget hasn't been instantiated yet.
      */
     static final int NO_ID = -1;
@@ -66,7 +79,7 @@
      */
     int appWidgetId = NO_ID;
 
-    ComponentName providerName;
+    public ComponentName providerName;
 
     /**
      * Indicates the restore status of the widget.
@@ -78,13 +91,12 @@
      */
     int installProgress = -1;
 
-    private boolean mHasNotifiedInitialWidgetSizeChanged;
-
     /**
-     * View that holds this widget after it's been created.  This view isn't created
-     * until Launcher knows it's needed.
+     * Optional extras sent during widget bind. See {@link #FLAG_DIRECT_CONFIG}.
      */
-    AppWidgetHostView hostView = null;
+    public Intent bindOptions;
+
+    private boolean mHasNotifiedInitialWidgetSizeChanged;
 
     LauncherAppWidgetInfo(int appWidgetId, ComponentName providerName) {
         if (appWidgetId == CUSTOM_WIDGET_ID) {
@@ -115,33 +127,29 @@
         values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
         values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName.flattenToString());
         values.put(LauncherSettings.Favorites.RESTORED, restoreStatus);
+        values.put(LauncherSettings.Favorites.INTENT,
+                bindOptions == null ? null : bindOptions.toUri(0));
     }
 
     /**
      * When we bind the widget, we should notify the widget that the size has changed if we have not
      * done so already (only really for default workspace widgets).
      */
-    void onBindAppWidget(Launcher launcher) {
+    void onBindAppWidget(Launcher launcher, AppWidgetHostView hostView) {
         if (!mHasNotifiedInitialWidgetSizeChanged) {
             AppWidgetResizeFrame.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
             mHasNotifiedInitialWidgetSizeChanged = true;
         }
     }
 
-
     @Override
-    public String toString() {
-        return "AppWidget(id=" + Integer.toString(appWidgetId) + ")";
+    protected String dumpProperties() {
+        return super.dumpProperties() + " appWidgetId=" + appWidgetId;
     }
 
-    @Override
-    void unbind() {
-        super.unbind();
-        hostView = null;
-    }
-
-    public final boolean isWidgetIdValid() {
-        return (restoreStatus & FLAG_ID_NOT_VALID) == 0;
+    public final boolean isWidgetIdAllocated() {
+        return (restoreStatus & FLAG_ID_NOT_VALID) == 0 ||
+                (restoreStatus & FLAG_ID_ALLOCATED) == FLAG_ID_ALLOCATED;
     }
 
     public final boolean hasRestoreFlag(int flag) {
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index 28d8052..1a4153f 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -63,19 +63,18 @@
         LauncherAppState app = LauncherAppState.getInstance();
         InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
 
-        // We only care out the cell size, which is independent of the the layout direction.
-        Rect paddingLand = idp.landscapeProfile.getWorkspacePadding(false /* isLayoutRtl */);
-        Rect paddingPort = idp.portraitProfile.getWorkspacePadding(false /* isLayoutRtl */);
+        Point paddingLand = idp.landscapeProfile.getTotalWorkspacePadding();
+        Point paddingPort = idp.portraitProfile.getTotalWorkspacePadding();
 
         // Always assume we're working with the smallest span to make sure we
         // reserve enough space in both orientations.
         float smallestCellWidth = DeviceProfile.calculateCellWidth(Math.min(
-                idp.landscapeProfile.widthPx - paddingLand.left - paddingLand.right,
-                idp.portraitProfile.widthPx - paddingPort.left - paddingPort.right),
+                idp.landscapeProfile.widthPx - paddingLand.x,
+                idp.portraitProfile.widthPx - paddingPort.x),
                 idp.numColumns);
         float smallestCellHeight = DeviceProfile.calculateCellWidth(Math.min(
-                idp.landscapeProfile.heightPx - paddingLand.top - paddingLand.bottom,
-                idp.portraitProfile.heightPx - paddingPort.top - paddingPort.bottom),
+                idp.landscapeProfile.heightPx - paddingLand.y,
+                idp.portraitProfile.heightPx - paddingPort.y),
                 idp.numRows);
 
         // We want to account for the extra amount of padding that we are adding to the widget
diff --git a/src/com/android/launcher3/LauncherBackupAgent.java b/src/com/android/launcher3/LauncherBackupAgent.java
new file mode 100644
index 0000000..b3e73f7
--- /dev/null
+++ b/src/com/android/launcher3/LauncherBackupAgent.java
@@ -0,0 +1,28 @@
+package com.android.launcher3;
+
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.os.ParcelFileDescriptor;
+
+import com.android.launcher3.provider.RestoreDbTask;
+
+public class LauncherBackupAgent extends BackupAgent {
+
+    @Override
+    public void onRestore(
+            BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) {
+        // Doesn't do incremental backup/restore
+    }
+
+    @Override
+    public void onBackup(
+            ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) {
+        // Doesn't do incremental backup/restore
+    }
+
+    @Override
+    public void onRestoreFinished() {
+        RestoreDbTask.setPending(this, true);
+    }
+}
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
deleted file mode 100644
index 1703e41..0000000
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.app.backup.BackupAgentHelper;
-import android.app.backup.BackupDataInput;
-import android.app.backup.BackupManager;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-
-import com.android.launcher3.model.GridSizeMigrationTask;
-
-import java.io.IOException;
-
-public class LauncherBackupAgentHelper extends BackupAgentHelper {
-
-    private static final String TAG = "LauncherBAHelper";
-
-    private static final String KEY_LAST_NOTIFIED_TIME = "backup_manager_last_notified";
-
-    private static final String LAUNCHER_DATA_PREFIX = "L";
-
-    static final boolean VERBOSE = false;
-    static final boolean DEBUG = false;
-
-    /**
-     * Notify the backup manager that out database is dirty.
-     *
-     * <P>This does not force an immediate backup.
-     *
-     * @param context application context
-     */
-    public static void dataChanged(Context context) {
-        dataChanged(context, 0);
-    }
-
-    /**
-     * Notify the backup manager that out database is dirty.
-     *
-     * <P>This does not force an immediate backup.
-     *
-     * @param context application context
-     * @param throttleMs duration in ms for which two consecutive calls to backup manager should
-     *                   not be made.
-     */
-    public static void dataChanged(Context context, long throttleMs) {
-        SharedPreferences prefs = Utilities.getPrefs(context);
-        long now = System.currentTimeMillis();
-        long lastTime = prefs.getLong(KEY_LAST_NOTIFIED_TIME, 0);
-
-        // User can manually change the system time, which could lead to now < lastTime.
-        // Re-backup in that case, as the backup will have a wrong lastModifiedTime.
-        if (now < lastTime || now >= (lastTime + throttleMs)) {
-            BackupManager.dataChanged(context.getPackageName());
-            prefs.edit().putLong(KEY_LAST_NOTIFIED_TIME, now).apply();
-        }
-    }
-
-    private LauncherBackupHelper mHelper;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mHelper = new LauncherBackupHelper(this);
-        addHelper(LAUNCHER_DATA_PREFIX, mHelper);
-    }
-
-    @Override
-    public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
-            throws IOException {
-        if (!Utilities.ATLEAST_LOLLIPOP) {
-            // 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;
-        }
-
-        // Clear dB before restore
-        LauncherAppState.getLauncherProvider().createEmptyDB();
-
-        boolean hasData;
-        try {
-            super.onRestore(data, appVersionCode, newState);
-            // If no favorite was migrated, clear the data and start fresh.
-            final Cursor c = getContentResolver().query(
-                    LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
-            hasData = c.moveToNext();
-            c.close();
-        } catch (Exception e) {
-            // If the restore fails, we should do a fresh start.
-            Log.e(TAG, "Restore failed", e);
-            hasData = false;
-        }
-
-        if (hasData && mHelper.restoreSuccessful) {
-            LauncherAppState.getLauncherProvider().clearFlagEmptyDbCreated();
-            LauncherClings.markFirstRunClingDismissed(this);
-
-            // Rank was added in v4.
-            if (mHelper.restoredBackupVersion <= 3) {
-                LauncherAppState.getLauncherProvider().updateFolderItemsRank();
-            }
-
-            if (GridSizeMigrationTask.ENABLED && mHelper.shouldAttemptWorkspaceMigration()) {
-                GridSizeMigrationTask.markForMigration(getApplicationContext(),
-                        mHelper.widgetSizes, mHelper.migrationCompatibleProfileData);
-            }
-
-            LauncherAppState.getLauncherProvider().convertShortcutsToLauncherActivities();
-        } else {
-            if (VERBOSE) Log.v(TAG, "Nothing was restored, clearing DB");
-            LauncherAppState.getLauncherProvider().createEmptyDB();
-        }
-    }
-}
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
deleted file mode 100644
index ea67ac8..0000000
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ /dev/null
@@ -1,1257 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3;
-
-import android.app.backup.BackupDataInputStream;
-import android.app.backup.BackupDataOutput;
-import android.app.backup.BackupHelper;
-import android.app.backup.BackupManager;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.res.XmlResourceParser;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Point;
-import android.graphics.drawable.Drawable;
-import android.os.ParcelFileDescriptor;
-import android.text.TextUtils;
-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.nano.BackupProtos;
-import com.android.launcher3.backup.nano.BackupProtos.CheckedMessage;
-import com.android.launcher3.backup.nano.BackupProtos.DeviceProfieData;
-import com.android.launcher3.backup.nano.BackupProtos.Favorite;
-import com.android.launcher3.backup.nano.BackupProtos.Journal;
-import com.android.launcher3.backup.nano.BackupProtos.Key;
-import com.android.launcher3.backup.nano.BackupProtos.Resource;
-import com.android.launcher3.backup.nano.BackupProtos.Screen;
-import com.android.launcher3.backup.nano.BackupProtos.Widget;
-import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.UserHandleCompat;
-import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.model.GridSizeMigrationTask;
-import com.android.launcher3.util.Thunk;
-import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
-import com.google.protobuf.nano.MessageNano;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.zip.CRC32;
-
-/**
- * Persist the launcher home state across calamities.
- */
-public class LauncherBackupHelper implements BackupHelper {
-    private static final String TAG = "LauncherBackupHelper";
-    private static final boolean VERBOSE = LauncherBackupAgentHelper.VERBOSE;
-    private static final boolean DEBUG = LauncherBackupAgentHelper.DEBUG;
-
-    private static final int BACKUP_VERSION = 4;
-    private static final int MAX_JOURNAL_SIZE = 1000000;
-
-    // Journal key is such that it is always smaller than any dynamically generated
-    // key (any Base64 encoded string).
-    private static final String JOURNAL_KEY = "#";
-
-    /** icons are large, dribble them out */
-    private static final int MAX_ICONS_PER_PASS = 10;
-
-    /** widgets contain previews, which are very large, dribble them out */
-    private static final int MAX_WIDGETS_PER_PASS = 5;
-
-    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.RANK,                    // 18
-    };
-
-    private static final int ID_INDEX = 0;
-    private static final int ID_MODIFIED = 1;
-    private static final int INTENT_INDEX = 2;
-    private static final int APPWIDGET_PROVIDER_INDEX = 3;
-    private static final int APPWIDGET_ID_INDEX = 4;
-    private static final int CELLX_INDEX = 5;
-    private static final int CELLY_INDEX = 6;
-    private static final int CONTAINER_INDEX = 7;
-    private static final int ICON_INDEX = 8;
-    private static final int ICON_PACKAGE_INDEX = 9;
-    private static final int ICON_RESOURCE_INDEX = 10;
-    private static final int ICON_TYPE_INDEX = 11;
-    private static final int ITEM_TYPE_INDEX = 12;
-    private static final int SCREEN_INDEX = 13;
-    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 RANK_INDEX = 18;
-
-    private static final String[] SCREEN_PROJECTION = {
-        WorkspaceScreens._ID,              // 0
-        WorkspaceScreens.MODIFIED,         // 1
-        WorkspaceScreens.SCREEN_RANK       // 2
-    };
-
-    private static final int SCREEN_RANK_INDEX = 2;
-
-    @Thunk final Context mContext;
-    private final HashSet<String> mExistingKeys;
-    private final ArrayList<Key> mKeys;
-    private final ItemTypeMatcher[] mItemTypeMatchers;
-    private final long mUserSerial;
-
-    private BackupManager mBackupManager;
-    private byte[] mBuffer = new byte[512];
-    private long mLastBackupRestoreTime;
-    private boolean mBackupDataWasUpdated;
-
-    private IconCache mIconCache;
-    private DeviceProfieData mDeviceProfileData;
-    private InvariantDeviceProfile mIdp;
-
-    DeviceProfieData migrationCompatibleProfileData;
-    HashSet<String> widgetSizes = new HashSet<>();
-
-    boolean restoreSuccessful;
-    int restoredBackupVersion = 1;
-
-    // When migrating from a device which different hotseat configuration, the icons are shifted
-    // to center along the new all-apps icon.
-    private int mHotseatShift = 0;
-
-    public LauncherBackupHelper(Context context) {
-        mContext = context;
-        mExistingKeys = new HashSet<String>();
-        mKeys = new ArrayList<Key>();
-        restoreSuccessful = true;
-        mItemTypeMatchers = new ItemTypeMatcher[CommonAppTypeParser.SUPPORTED_TYPE_COUNT];
-
-        UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
-        mUserSerial = userManager.getSerialNumberForUser(UserHandleCompat.myUserHandle());
-    }
-
-    private void dataChanged() {
-        if (mBackupManager == null) {
-            mBackupManager = new BackupManager(mContext);
-        }
-        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));
-            }
-        }
-        restoredBackupVersion = journal.backupVersion;
-    }
-
-    /**
-     * Back up launcher data so we can restore the user's state on a new device.
-     *
-     * <P>The journal is a timestamp and a list of keys that were saved as of that time.
-     *
-     * <P>Keys may come back in any order, so each key/value is one complete row of the database.
-     *
-     * @param oldState notes from the last backup
-     * @param data incremental key/value pairs to persist off-device
-     * @param newState notes for the next backup
-     */
-    @Override
-    public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-            ParcelFileDescriptor newState) {
-        if (VERBOSE) Log.v(TAG, "onBackup");
-
-        Journal in = readJournal(oldState);
-        if (!launcherIsReady()) {
-            dataChanged();
-            // Perform backup later.
-            writeJournal(newState, in);
-            return;
-        }
-
-        if (mDeviceProfileData == null) {
-            LauncherAppState app = LauncherAppState.getInstance();
-            mIdp = app.getInvariantDeviceProfile();
-            mDeviceProfileData = initDeviceProfileData(mIdp);
-            mIconCache = app.getIconCache();
-        }
-
-        Log.v(TAG, "lastBackupTime = " + in.t);
-        mKeys.clear();
-        applyJournal(in);
-
-        // 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();
-        mBackupDataWasUpdated = false;
-        try {
-            backupFavorites(data);
-            backupScreens(data);
-            backupIcons(data);
-            backupWidgets(data);
-
-            // 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));
-            }
-            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);
-                mBackupDataWasUpdated = true;
-            }
-
-            mExistingKeys.clear();
-            if (!mBackupDataWasUpdated) {
-                // Check if any metadata has changed
-                mBackupDataWasUpdated = (in.profile == null)
-                        || !Arrays.equals(DeviceProfieData.toByteArray(in.profile),
-                            DeviceProfieData.toByteArray(mDeviceProfileData))
-                        || (in.backupVersion != BACKUP_VERSION)
-                        || (in.appVersion != getAppVersion());
-            }
-
-            if (mBackupDataWasUpdated) {
-                mLastBackupRestoreTime = newBackupTime;
-
-                // We store the journal at two places.
-                //   1) Storing it in newState allows us to do partial backups by comparing old state
-                //   2) Storing it in backup data allows us to validate keys during restore
-                Journal state = getCurrentStateJournal();
-                writeRowToBackup(JOURNAL_KEY, state, data);
-            } else {
-                if (DEBUG) Log.d(TAG, "Nothing was written during backup");
-            }
-        } catch (IOException e) {
-            Log.e(TAG, "launcher backup has failed", e);
-        }
-
-        writeNewStateDescription(newState);
-    }
-
-    /**
-     * @return true if the backup corresponding to oldstate can be successfully applied
-     * to this device.
-     */
-    private boolean isBackupCompatible(Journal oldState) {
-        DeviceProfieData currentProfile = mDeviceProfileData;
-        DeviceProfieData oldProfile = oldState.profile;
-
-        if (oldProfile == null || oldProfile.desktopCols == 0) {
-            // Profile info is not valid, ignore the check.
-            return true;
-        }
-
-        boolean isHotseatCompatible = false;
-        if (currentProfile.allappsRank >= oldProfile.hotseatCount) {
-            isHotseatCompatible = true;
-            mHotseatShift = 0;
-        }
-
-        if ((currentProfile.allappsRank >= oldProfile.allappsRank)
-                && ((currentProfile.hotseatCount - currentProfile.allappsRank) >=
-                        (oldProfile.hotseatCount - oldProfile.allappsRank))) {
-            // There is enough space on both sides of the hotseat.
-            isHotseatCompatible = true;
-            mHotseatShift = currentProfile.allappsRank - oldProfile.allappsRank;
-        }
-
-        if (!isHotseatCompatible) {
-            return false;
-        }
-        if ((currentProfile.desktopCols >= oldProfile.desktopCols)
-                && (currentProfile.desktopRows >= oldProfile.desktopRows)) {
-            return true;
-        }
-
-        if (GridSizeMigrationTask.ENABLED) {
-            // One time migrate the workspace when launcher starts.
-            migrationCompatibleProfileData = initDeviceProfileData(mIdp);
-            migrationCompatibleProfileData.desktopCols = oldProfile.desktopCols;
-            migrationCompatibleProfileData.desktopRows = oldProfile.desktopRows;
-            migrationCompatibleProfileData.hotseatCount = oldProfile.hotseatCount;
-            migrationCompatibleProfileData.allappsRank = oldProfile.allappsRank;
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Restore launcher configuration from the restored data stream.
-     * It assumes that the keys will arrive in lexical order. So if the journal was present in the
-     * backup, it should arrive first.
-     *
-     * @param data the key/value pair from the server
-     */
-    @Override
-    public void restoreEntity(BackupDataInputStream data) {
-        if (!restoreSuccessful) {
-            return;
-        }
-
-        if (mDeviceProfileData == null) {
-            // This call does not happen on a looper thread. So LauncherAppState
-            // can't be created . Instead initialize required dependencies directly.
-            mIdp = new InvariantDeviceProfile(mContext);
-            mDeviceProfileData = initDeviceProfileData(mIdp);
-            mIconCache = new IconCache(mContext, mIdp);
-        }
-
-        int dataSize = data.size();
-        if (mBuffer.length < dataSize) {
-            mBuffer = new byte[dataSize];
-        }
-        try {
-            int bytesRead = data.read(mBuffer, 0, dataSize);
-            if (DEBUG) Log.d(TAG, "read " + bytesRead + " of " + dataSize + " available");
-            String backupKey = data.getKey();
-
-            if (JOURNAL_KEY.equals(backupKey)) {
-                if (VERBOSE) Log.v(TAG, "Journal entry restored");
-                if (!mKeys.isEmpty()) {
-                    // We received the journal key after a restore key.
-                    Log.wtf(TAG, keyToBackupKey(mKeys.get(0)) + " received after " + JOURNAL_KEY);
-                    restoreSuccessful = false;
-                    return;
-                }
-
-                Journal journal = new Journal();
-                MessageNano.mergeFrom(journal, readCheckedBytes(mBuffer, dataSize));
-                applyJournal(journal);
-                restoreSuccessful = isBackupCompatible(journal);
-                return;
-            }
-
-            if (!mExistingKeys.isEmpty() && !mExistingKeys.contains(backupKey)) {
-                if (DEBUG) Log.e(TAG, "Ignoring key not present in the backup state " + backupKey);
-                return;
-            }
-            Key key = backupKeyToKey(backupKey);
-            mKeys.add(key);
-            switch (key.type) {
-                case Key.FAVORITE:
-                    restoreFavorite(key, mBuffer, dataSize);
-                    break;
-
-                case Key.SCREEN:
-                    restoreScreen(key, mBuffer, dataSize);
-                    break;
-
-                case Key.ICON:
-                    restoreIcon(key, mBuffer, dataSize);
-                    break;
-
-                case Key.WIDGET:
-                    restoreWidget(key, mBuffer, dataSize);
-                    break;
-
-                default:
-                    Log.w(TAG, "unknown restore entity type: " + key.type);
-                    mKeys.remove(key);
-                    break;
-            }
-        } catch (IOException e) {
-            Log.w(TAG, "ignoring unparsable backup entry", e);
-        }
-    }
-
-    /**
-     * Record the restore state for the next backup.
-     *
-     * @param newState notes about the backup state after restore.
-     */
-    @Override
-    public void writeNewStateDescription(ParcelFileDescriptor newState) {
-        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();
-        journal.backupVersion = BACKUP_VERSION;
-        journal.profile = mDeviceProfileData;
-        return journal;
-    }
-
-    private int getAppVersion() {
-        try {
-            return mContext.getPackageManager()
-                    .getPackageInfo(mContext.getPackageName(), 0).versionCode;
-        } catch (NameNotFoundException e) {
-            return 0;
-        }
-    }
-
-    private DeviceProfieData initDeviceProfileData(InvariantDeviceProfile profile) {
-        DeviceProfieData data = new DeviceProfieData();
-        data.desktopRows = profile.numRows;
-        data.desktopCols = profile.numColumns;
-        data.hotseatCount = profile.numHotseatIcons;
-        data.allappsRank = profile.hotseatAllAppsRank;
-        return data;
-    }
-
-    /**
-     * Write all modified favorites to the data stream.
-     *
-     * @param data output stream for key/value pairs
-     * @throws IOException
-     */
-    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);
-        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);
-                mKeys.add(key);
-                final String backupKey = keyToBackupKey(key);
-
-                // Favorite proto changed in v4. Backup again if the version is old.
-                if (!mExistingKeys.contains(backupKey) || updateTime >= mLastBackupRestoreTime
-                        || restoredBackupVersion < 4) {
-                    writeRowToBackup(key, packFavorite(cursor), data);
-                } else {
-                    if (DEBUG) Log.d(TAG, "favorite already backup up: " + id);
-                }
-            }
-        } finally {
-            cursor.close();
-        }
-    }
-
-    /**
-     * Read a favorite from the stream.
-     *
-     * <P>Keys arrive in any order, so screens and containers may not exist yet.
-     *
-     * @param key identifier for the row
-     * @param buffer the serialized proto from the stream, may be larger than dataSize
-     * @param dataSize the size of the proto from the stream
-     */
-    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));
-
-        ContentResolver cr = mContext.getContentResolver();
-        ContentValues values = unpackFavorite(buffer, dataSize);
-        cr.insert(Favorites.CONTENT_URI, values);
-    }
-
-    /**
-     * Write all modified screens to the data stream.
-     *
-     * @param data output stream for key/value pairs
-     * @throws IOException
-     */
-    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);
-        try {
-            cursor.moveToPosition(-1);
-            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);
-                mKeys.add(key);
-                final String backupKey = keyToBackupKey(key);
-                if (!mExistingKeys.contains(backupKey) || updateTime >= mLastBackupRestoreTime) {
-                    writeRowToBackup(key, packScreen(cursor), data);
-                } else {
-                    if (VERBOSE) Log.v(TAG, "screen already backup up " + id);
-                }
-            }
-        } finally {
-            cursor.close();
-        }
-    }
-
-    /**
-     * Read a screen from the stream.
-     *
-     * <P>Keys arrive in any order, so children of this screen may already exist.
-     *
-     * @param key identifier for the row
-     * @param buffer the serialized proto from the stream, may be larger than dataSize
-     * @param dataSize the size of the proto from the stream
-     */
-    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));
-
-        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 data output stream for key/value pairs
-     */
-    private void backupIcons(BackupDataOutput data) throws IOException {
-        // persist icons that haven't been persisted yet
-        final ContentResolver cr = mContext.getContentResolver();
-        final int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
-        final UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
-        int backupUpIconCount = 0;
-
-        // Don't backup apps in other profiles for now.
-        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);
-        try {
-            cursor.moveToPosition(-1);
-            while(cursor.moveToNext()) {
-                final long id = cursor.getLong(ID_INDEX);
-                final String intentDescription = cursor.getString(INTENT_INDEX);
-                try {
-                    Intent intent = Intent.parseUri(intentDescription, 0);
-                    ComponentName cn = intent.getComponent();
-                    Key key = null;
-                    String backupKey = null;
-                    if (cn != null) {
-                        key = getKey(Key.ICON, cn.flattenToShortString());
-                        backupKey = keyToBackupKey(key);
-                    } else {
-                        Log.w(TAG, "empty intent on application favorite: " + id);
-                    }
-                    if (mExistingKeys.contains(backupKey)) {
-                        if (DEBUG) Log.d(TAG, "already saved icon " + backupKey);
-
-                        // remember that we already backed this up previously
-                        mKeys.add(key);
-                    } else if (backupKey != null) {
-                        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);
-                            if (icon != null && !mIconCache.isDefaultIcon(icon, myUserHandle)) {
-                                writeRowToBackup(key, packIcon(dpi, icon), data);
-                                mKeys.add(key);
-                                backupUpIconCount ++;
-                            }
-                        } else {
-                            if (VERBOSE) Log.v(TAG, "deferring icon backup " + backupKey);
-                            // too many icons for this pass, request another.
-                            dataChanged();
-                        }
-                    }
-                } catch (URISyntaxException e) {
-                    Log.e(TAG, "invalid URI on application favorite: " + id);
-                } catch (IOException e) {
-                    Log.e(TAG, "unable to save application icon for favorite: " + id);
-                }
-
-            }
-        } finally {
-            cursor.close();
-        }
-    }
-
-    /**
-     * Read an icon from the stream.
-     *
-     * <P>Keys arrive in any order, so shortcuts that use this icon may already exist.
-     *
-     * @param key identifier for the row
-     * @param buffer the serialized proto from the stream, may be larger than dataSize
-     * @param dataSize the size of the proto from the stream
-     */
-    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));
-
-        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);
-        } else {
-            if (VERBOSE) Log.v(TAG, "saving restored icon as: " + key.name);
-            mIconCache.preloadIcon(ComponentName.unflattenFromString(key.name), icon, res.dpi,
-                    "" /* label */, mUserSerial, mIdp);
-        }
-    }
-
-    /**
-     * Write all the static widget resources we need to render placeholders
-     * for a package that is not installed.
-     *
-     * @param data output stream for key/value pairs
-     * @throws IOException
-     */
-    private void backupWidgets(BackupDataOutput data) throws IOException {
-        // persist static widget info that hasn't been persisted yet
-        final ContentResolver cr = mContext.getContentResolver();
-        final int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
-        int backupWidgetCount = 0;
-
-        String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPWIDGET + " AND "
-                + getUserSelectionArg();
-        Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
-                where, null, null);
-        AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(mContext);
-        try {
-            cursor.moveToPosition(-1);
-            while(cursor.moveToNext()) {
-                final long id = cursor.getLong(ID_INDEX);
-                final String providerName = cursor.getString(APPWIDGET_PROVIDER_INDEX);
-                final ComponentName provider = ComponentName.unflattenFromString(providerName);
-
-                Key key = null;
-                String backupKey = null;
-                if (provider != null) {
-                    key = getKey(Key.WIDGET, providerName);
-                    backupKey = keyToBackupKey(key);
-                } else {
-                    Log.w(TAG, "empty intent on appwidget: " + id);
-                }
-
-                // Widget backup proto changed in v3. So add it again if the original backup is old.
-                if (mExistingKeys.contains(backupKey) && restoredBackupVersion >= 3) {
-                    if (DEBUG) Log.d(TAG, "already saved widget " + backupKey);
-
-                    // remember that we already backed this up previously
-                    mKeys.add(key);
-                } else if (backupKey != null) {
-                    if (DEBUG) Log.d(TAG, "I can count this high: " + backupWidgetCount);
-                    if (backupWidgetCount < MAX_WIDGETS_PER_PASS) {
-                        LauncherAppWidgetProviderInfo widgetInfo = widgetManager
-                                .getLauncherAppWidgetInfo(cursor.getInt(APPWIDGET_ID_INDEX));
-                        if (widgetInfo != null) {
-                            if (DEBUG) Log.d(TAG, "saving widget " + backupKey);
-                            writeRowToBackup(key, packWidget(dpi, widgetInfo), data);
-                            mKeys.add(key);
-                            backupWidgetCount ++;
-                        }
-                    } else {
-                        if (VERBOSE) Log.v(TAG, "deferring widget backup " + backupKey);
-                        // too many widgets for this pass, request another.
-                        dataChanged();
-                    }
-                }
-            }
-        } finally {
-            cursor.close();
-        }
-    }
-
-    /**
-     * Read a widget from the stream.
-     *
-     * <P>Keys arrive in any order, so widgets that use this data may already exist.
-     *
-     * @param key identifier for the row
-     * @param buffer the serialized proto from the stream, may be larger than dataSize
-     * @param dataSize the size of the proto from the stream
-     */
-    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));
-        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 {
-                mIconCache.preloadIcon(ComponentName.unflattenFromString(widget.provider),
-                        icon, widget.icon.dpi, widget.label, mUserSerial, mIdp);
-            }
-        }
-
-        // Cache widget min sizes incase migration is required.
-        widgetSizes.add(widget.provider + "#" + widget.minSpanX + "," + widget.minSpanY);
-    }
-
-    /** create a new key, with an integer ID.
-     *
-     * <P> Keys contain their own checksum instead of using
-     * the heavy-weight CheckedMessage wrapper.
-     */
-    private Key getKey(int type, long id) {
-        Key key = new Key();
-        key.type = type;
-        key.id = id;
-        key.checksum = checkKey(key);
-        return key;
-    }
-
-    /** create a new key for a named object.
-     *
-     * <P> Keys contain their own checksum instead of using
-     * the heavy-weight CheckedMessage wrapper.
-     */
-    private Key getKey(int type, String name) {
-        Key key = new Key();
-        key.type = type;
-        key.name = name;
-        key.checksum = checkKey(key);
-        return key;
-    }
-
-    /** keys need to be strings, serialize and encode. */
-    private String keyToBackupKey(Key key) {
-        return Base64.encodeToString(Key.toByteArray(key), Base64.NO_WRAP);
-    }
-
-    /** keys need to be strings, decode and parse. */
-    private Key backupKeyToKey(String backupKey) throws InvalidBackupException {
-        try {
-            Key key = Key.parseFrom(Base64.decode(backupKey, Base64.DEFAULT));
-            if (key.checksum != checkKey(key)) {
-                throw new InvalidBackupException("invalid key read from stream" + backupKey);
-            }
-            return key;
-        } catch (InvalidProtocolBufferNanoException | IllegalArgumentException e) {
-            throw new InvalidBackupException(e);
-        }
-    }
-
-    /** Compute the checksum over the important bits of a key. */
-    private long checkKey(Key key) {
-        CRC32 checksum = new CRC32();
-        checksum.update(key.type);
-        checksum.update((int) (key.id & 0xffff));
-        checksum.update((int) ((key.id >> 32) & 0xffff));
-        if (!TextUtils.isEmpty(key.name)) {
-            checksum.update(key.name.getBytes());
-        }
-        return checksum.getValue();
-    }
-
-    /**
-     * @return true if its an hotseat item, that can be replaced during restore.
-     * TODO: Extend check for folders in hotseat.
-     */
-    private boolean isReplaceableHotseatItem(Favorite favorite) {
-        return favorite.container == Favorites.CONTAINER_HOTSEAT
-                && favorite.intent != null
-                && (favorite.itemType == Favorites.ITEM_TYPE_APPLICATION
-                || favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT);
-    }
-
-    /** Serialize a Favorite for persistence, including a checksum wrapper. */
-    private Favorite packFavorite(Cursor c) {
-        Favorite favorite = new Favorite();
-        favorite.id = c.getLong(ID_INDEX);
-        favorite.screen = c.getInt(SCREEN_INDEX);
-        favorite.container = c.getInt(CONTAINER_INDEX);
-        favorite.cellX = c.getInt(CELLX_INDEX);
-        favorite.cellY = c.getInt(CELLY_INDEX);
-        favorite.spanX = c.getInt(SPANX_INDEX);
-        favorite.spanY = c.getInt(SPANY_INDEX);
-        favorite.iconType = c.getInt(ICON_TYPE_INDEX);
-        favorite.rank = c.getInt(RANK_INDEX);
-
-        String title = c.getString(TITLE_INDEX);
-        if (!TextUtils.isEmpty(title)) {
-            favorite.title = title;
-        }
-        String intentDescription = c.getString(INTENT_INDEX);
-        Intent intent = null;
-        if (!TextUtils.isEmpty(intentDescription)) {
-            try {
-                intent = Intent.parseUri(intentDescription, 0);
-                intent.removeExtra(ItemInfo.EXTRA_PROFILE);
-                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) {
-            favorite.appWidgetId = c.getInt(APPWIDGET_ID_INDEX);
-            String appWidgetProvider = c.getString(APPWIDGET_PROVIDER_INDEX);
-            if (!TextUtils.isEmpty(appWidgetProvider)) {
-                favorite.appWidgetProvider = appWidgetProvider;
-            }
-        } else if (favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT) {
-            if (favorite.iconType == Favorites.ICON_TYPE_RESOURCE) {
-                String iconPackage = c.getString(ICON_PACKAGE_INDEX);
-                if (!TextUtils.isEmpty(iconPackage)) {
-                    favorite.iconPackage = iconPackage;
-                }
-                String iconResource = c.getString(ICON_RESOURCE_INDEX);
-                if (!TextUtils.isEmpty(iconResource)) {
-                    favorite.iconResource = iconResource;
-                }
-            }
-
-            byte[] blob = c.getBlob(ICON_INDEX);
-            if (blob != null && blob.length > 0) {
-                favorite.icon = blob;
-            }
-        }
-
-        if (isReplaceableHotseatItem(favorite)) {
-            if (intent != null && intent.getComponent() != null) {
-                PackageManager pm = mContext.getPackageManager();
-                ActivityInfo activity = null;;
-                try {
-                    activity = pm.getActivityInfo(intent.getComponent(), 0);
-                } catch (NameNotFoundException e) {
-                    Log.e(TAG, "Target not found", e);
-                }
-                if (activity == null) {
-                    return favorite;
-                }
-                for (int i = 0; i < mItemTypeMatchers.length; i++) {
-                    if (mItemTypeMatchers[i] == null) {
-                        mItemTypeMatchers[i] = new ItemTypeMatcher(
-                                CommonAppTypeParser.getResourceForItemType(i));
-                    }
-                    if (mItemTypeMatchers[i].matches(activity, pm)) {
-                        favorite.targetType = i;
-                        break;
-                    }
-                }
-            }
-        }
-
-        return favorite;
-    }
-
-    /** Deserialize a Favorite from persistence, after verifying checksum wrapper. */
-    private ContentValues unpackFavorite(byte[] buffer, int dataSize)
-            throws IOException {
-        Favorite favorite = unpackProto(new Favorite(), buffer, dataSize);
-
-        // If it is a hotseat item, move it accordingly.
-        if (favorite.container == Favorites.CONTAINER_HOTSEAT) {
-            favorite.screen += mHotseatShift;
-        }
-
-        ContentValues values = new ContentValues();
-        values.put(Favorites._ID, favorite.id);
-        values.put(Favorites.SCREEN, favorite.screen);
-        values.put(Favorites.CONTAINER, favorite.container);
-        values.put(Favorites.CELLX, favorite.cellX);
-        values.put(Favorites.CELLY, favorite.cellY);
-        values.put(Favorites.SPANX, favorite.spanX);
-        values.put(Favorites.SPANY, favorite.spanY);
-        values.put(Favorites.RANK, favorite.rank);
-
-        if (favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT) {
-            values.put(Favorites.ICON_TYPE, favorite.iconType);
-            if (favorite.iconType == Favorites.ICON_TYPE_RESOURCE) {
-                values.put(Favorites.ICON_PACKAGE, favorite.iconPackage);
-                values.put(Favorites.ICON_RESOURCE, favorite.iconResource);
-            }
-            values.put(Favorites.ICON, favorite.icon);
-        }
-
-        if (!TextUtils.isEmpty(favorite.title)) {
-            values.put(Favorites.TITLE, favorite.title);
-        } else {
-            values.put(Favorites.TITLE, "");
-        }
-        if (!TextUtils.isEmpty(favorite.intent)) {
-            values.put(Favorites.INTENT, favorite.intent);
-        }
-        values.put(Favorites.ITEM_TYPE, favorite.itemType);
-
-        UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
-        long userSerialNumber =
-                UserManagerCompat.getInstance(mContext).getSerialNumberForUser(myUserHandle);
-        values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber);
-
-        // If we will attempt grid resize, use the original profile to validate grid size, as
-        // anything which fits in the original grid should fit in the current grid after
-        // grid migration.
-        DeviceProfieData currentProfile = migrationCompatibleProfileData == null
-                ? mDeviceProfileData : migrationCompatibleProfileData;
-
-        if (favorite.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
-            if (!TextUtils.isEmpty(favorite.appWidgetProvider)) {
-                values.put(Favorites.APPWIDGET_PROVIDER, favorite.appWidgetProvider);
-            }
-            values.put(Favorites.APPWIDGET_ID, favorite.appWidgetId);
-            values.put(LauncherSettings.Favorites.RESTORED,
-                    LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
-                    LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
-                    LauncherAppWidgetInfo.FLAG_UI_NOT_READY);
-
-            // Verify placement
-            if (((favorite.cellX + favorite.spanX) > currentProfile.desktopCols)
-                    || ((favorite.cellY + favorite.spanY) > currentProfile.desktopRows)) {
-                restoreSuccessful = false;
-                throw new InvalidBackupException("Widget not in screen bounds, aborting restore");
-            }
-        } else {
-            // Check if it is an hotseat item, that can be replaced.
-            if (isReplaceableHotseatItem(favorite)
-                    && favorite.targetType != Favorite.TARGET_NONE
-                    && favorite.targetType < CommonAppTypeParser.SUPPORTED_TYPE_COUNT) {
-                Log.e(TAG, "Added item type flag");
-                values.put(LauncherSettings.Favorites.RESTORED,
-                        1 | CommonAppTypeParser.encodeItemTypeToFlag(favorite.targetType));
-            } else {
-                // Let LauncherModel know we've been here.
-                values.put(LauncherSettings.Favorites.RESTORED, 1);
-            }
-
-            // Verify placement
-            if (favorite.container == Favorites.CONTAINER_HOTSEAT) {
-                if ((favorite.screen >= currentProfile.hotseatCount)
-                        || (favorite.screen == currentProfile.allappsRank)) {
-                    restoreSuccessful = false;
-                    throw new InvalidBackupException("Item not in hotseat bounds, aborting restore");
-                }
-            } else {
-                if ((favorite.cellX >= currentProfile.desktopCols)
-                        || (favorite.cellY >= currentProfile.desktopRows)) {
-                    restoreSuccessful = false;
-                    throw new InvalidBackupException("Item not in desktop bounds, aborting restore");
-                }
-            }
-        }
-
-        return values;
-    }
-
-    /** Serialize a Screen for persistence, including a checksum wrapper. */
-    private Screen packScreen(Cursor c) {
-        Screen screen = new Screen();
-        screen.id = c.getLong(ID_INDEX);
-        screen.rank = c.getInt(SCREEN_RANK_INDEX);
-        return screen;
-    }
-
-    /** Deserialize a Screen from persistence, after verifying checksum wrapper. */
-    private ContentValues unpackScreen(byte[] buffer, int dataSize)
-            throws InvalidProtocolBufferNanoException {
-        Screen screen = unpackProto(new Screen(), buffer, dataSize);
-        ContentValues values = new ContentValues();
-        values.put(WorkspaceScreens._ID, screen.id);
-        values.put(WorkspaceScreens.SCREEN_RANK, screen.rank);
-        return values;
-    }
-
-    /** Serialize an icon Resource for persistence, including a checksum wrapper. */
-    private Resource packIcon(int dpi, Bitmap icon) {
-        Resource res = new Resource();
-        res.dpi = dpi;
-        res.data = Utilities.flattenBitmap(icon);
-        return res;
-    }
-
-    /** Serialize a widget for persistence, including a checksum wrapper. */
-    private Widget packWidget(int dpi, LauncherAppWidgetProviderInfo info) {
-        Widget widget = new Widget();
-        widget.provider = info.provider.flattenToShortString();
-        widget.label = info.label;
-        widget.configure = info.configure != null;
-        if (info.icon != 0) {
-            widget.icon = new Resource();
-            Drawable fullResIcon = mIconCache.getFullResIcon(info.provider.getPackageName(), info.icon);
-            Bitmap icon = Utilities.createIconBitmap(fullResIcon, mContext);
-            widget.icon.data = Utilities.flattenBitmap(icon);
-            widget.icon.dpi = dpi;
-        }
-
-        Point spans = info.getMinSpans(mIdp, mContext);
-        widget.minSpanX = spans.x;
-        widget.minSpanY = spans.y;
-        return widget;
-    }
-
-    /**
-     * Deserialize a proto after verifying checksum wrapper.
-     */
-    private <T extends MessageNano> T unpackProto(T proto, byte[] buffer, int dataSize)
-            throws InvalidProtocolBufferNanoException {
-        MessageNano.mergeFrom(proto, readCheckedBytes(buffer, dataSize));
-        if (DEBUG) Log.d(TAG, "unpacked proto " + proto);
-        return proto;
-    }
-
-    /**
-     * Read the old journal from the input file.
-     *
-     * In the event of any error, just pretend we didn't have a journal,
-     * in that case, do a full backup.
-     *
-     * @param oldState the read-0only file descriptor pointing to the old journal
-     * @return a Journal protocol buffer
-     */
-    private Journal readJournal(ParcelFileDescriptor oldState) {
-        Journal journal = new Journal();
-        if (oldState == null) {
-            return journal;
-        }
-        FileInputStream inStream = new FileInputStream(oldState.getFileDescriptor());
-        try {
-            int availableBytes = inStream.available();
-            if (DEBUG) Log.d(TAG, "available " + availableBytes);
-            if (availableBytes < MAX_JOURNAL_SIZE) {
-                byte[] buffer = new byte[availableBytes];
-                int bytesRead = 0;
-                boolean valid = false;
-                InvalidProtocolBufferNanoException lastProtoException = null;
-                while (availableBytes > 0) {
-                    try {
-                        // OMG what are you doing? This is crazy inefficient!
-                        // If we read a byte that is not ours, we will cause trouble: b/12491813
-                        // However, we don't know how many bytes to expect (oops).
-                        // So we have to step through *slowly*, watching for the end.
-                        int result = inStream.read(buffer, bytesRead, 1);
-                        if (result > 0) {
-                            availableBytes -= result;
-                            bytesRead += result;
-                        } else {
-                            Log.w(TAG, "unexpected end of file while reading journal.");
-                            // stop reading and see what there is to parse
-                            availableBytes = 0;
-                        }
-                    } catch (IOException e) {
-                        buffer = null;
-                        availableBytes = 0;
-                    }
-
-                    // check the buffer to see if we have a valid journal
-                    try {
-                        MessageNano.mergeFrom(journal, readCheckedBytes(buffer, bytesRead));
-                        // if we are here, then we have read a valid, checksum-verified journal
-                        valid = true;
-                        availableBytes = 0;
-                        if (VERBOSE) Log.v(TAG, "read " + bytesRead + " bytes of journal");
-                    } catch (InvalidProtocolBufferNanoException e) {
-                        // if we don't have the whole journal yet, mergeFrom will throw. keep going.
-                        lastProtoException = e;
-                        journal.clear();
-                    }
-                }
-                if (DEBUG) Log.d(TAG, "journal bytes read: " + bytesRead);
-                if (!valid) {
-                    Log.w(TAG, "could not find a valid journal", lastProtoException);
-                }
-            }
-        } catch (IOException e) {
-            Log.w(TAG, "failed to close the journal", e);
-        }
-        return journal;
-    }
-
-    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 {
-        byte[] blob = writeCheckedBytes(proto);
-        data.writeEntityHeader(backupKey, blob.length);
-        data.writeEntityData(blob, blob.length);
-        mBackupDataWasUpdated = true;
-        if (VERBOSE) Log.v(TAG, "Writing New entry " + backupKey);
-    }
-
-    /**
-     * Write the new journal to the output file.
-     *
-     * In the event of any error, just pretend we didn't have a journal,
-     * in that case, do a full backup.
-
-     * @param newState the write-only file descriptor pointing to the new journal
-     * @param journal a Journal protocol buffer
-     */
-    private void writeJournal(ParcelFileDescriptor newState, Journal journal) {
-        try {
-            FileOutputStream outStream = new FileOutputStream(newState.getFileDescriptor());
-            final byte[] journalBytes = writeCheckedBytes(journal);
-            outStream.write(journalBytes);
-            if (VERBOSE) Log.v(TAG, "wrote " + journalBytes.length + " bytes of journal");
-        } catch (IOException e) {
-            Log.w(TAG, "failed to write backup journal", e);
-        }
-    }
-
-    /** Wrap a proto in a CheckedMessage and compute the checksum. */
-    private byte[] writeCheckedBytes(MessageNano proto) {
-        CheckedMessage wrapper = new CheckedMessage();
-        wrapper.payload = MessageNano.toByteArray(proto);
-        CRC32 checksum = new CRC32();
-        checksum.update(wrapper.payload);
-        wrapper.checksum = checksum.getValue();
-        return MessageNano.toByteArray(wrapper);
-    }
-
-    /** Unwrap a proto message from a CheckedMessage, verifying the checksum. */
-    private static byte[] readCheckedBytes(byte[] buffer, int dataSize)
-            throws InvalidProtocolBufferNanoException {
-        CheckedMessage wrapper = new CheckedMessage();
-        MessageNano.mergeFrom(wrapper, buffer, 0, dataSize);
-        CRC32 checksum = new CRC32();
-        checksum.update(wrapper.payload);
-        if (wrapper.checksum != checksum.getValue()) {
-            throw new InvalidProtocolBufferNanoException("checksum does not match");
-        }
-        return wrapper.payload;
-    }
-
-    /**
-     * @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);
-        if (cursor == null) {
-            // launcher data has been wiped, do nothing
-            return false;
-        }
-        cursor.close();
-
-        if (LauncherAppState.getInstanceNoCreate() == null) {
-            // launcher services are unavailable, try again later
-            return false;
-        }
-
-        return true;
-    }
-
-    private String getUserSelectionArg() {
-        return Favorites.PROFILE_ID + '=' + UserManagerCompat.getInstance(mContext)
-                .getSerialNumberForUser(UserHandleCompat.myUserHandle());
-    }
-
-    @Thunk class InvalidBackupException extends IOException {
-
-        private static final long serialVersionUID = 8931456637211665082L;
-
-        @Thunk InvalidBackupException(Throwable cause) {
-            super(cause);
-        }
-
-        @Thunk InvalidBackupException(String reason) {
-            super(reason);
-        }
-    }
-
-    public boolean shouldAttemptWorkspaceMigration() {
-        return migrationCompatibleProfileData != null;
-    }
-
-    /**
-     * A class to check if an activity can handle one of the intents from a list of
-     * predefined intents.
-     */
-    private class ItemTypeMatcher {
-
-        private final ArrayList<Intent> mIntents;
-
-        ItemTypeMatcher(int xml_res) {
-            mIntents = xml_res == 0 ? new ArrayList<Intent>() : parseIntents(xml_res);
-        }
-
-        private ArrayList<Intent> parseIntents(int xml_res) {
-            ArrayList<Intent> intents = new ArrayList<Intent>();
-            XmlResourceParser parser = mContext.getResources().getXml(xml_res);
-            try {
-                DefaultLayoutParser.beginDocument(parser, DefaultLayoutParser.TAG_RESOLVE);
-                final int depth = parser.getDepth();
-                int type;
-                while (((type = parser.next()) != XmlPullParser.END_TAG ||
-                        parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
-                    if (type != XmlPullParser.START_TAG) {
-                        continue;
-                    } else if (DefaultLayoutParser.TAG_FAVORITE.equals(parser.getName())) {
-                        final String uri = DefaultLayoutParser.getAttributeValue(
-                                parser, DefaultLayoutParser.ATTR_URI);
-                        intents.add(Intent.parseUri(uri, 0));
-                    }
-                }
-            } catch (URISyntaxException | XmlPullParserException | IOException e) {
-                Log.e(TAG, "Unable to parse " + xml_res, e);
-            } finally {
-                parser.close();
-            }
-            return intents;
-        }
-
-        public boolean matches(ActivityInfo activity, PackageManager pm) {
-            for (Intent intent : mIntents) {
-                intent.setPackage(activity.packageName);
-                ResolveInfo info = pm.resolveActivity(intent, 0);
-                if (info != null && (info.activityInfo.name.equals(activity.name)
-                        || info.activityInfo.name.equals(activity.targetActivity))) {
-                    return true;
-                }
-            }
-            return false;
-        }
-    }
-}
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 069deb4..6394b90 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -1,13 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.launcher3;
 
-import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.View;
-import android.view.ViewGroup;
+
 import com.android.launcher3.allapps.AllAppsSearchBarController;
+import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.util.ComponentKey;
 
 import java.io.FileDescriptor;
@@ -56,26 +72,18 @@
      */
     public void onLauncherProviderChange();
     public void finishBindingItems(final boolean upgradePath);
-    public void onClickAllAppsButton(View v);
     public void bindAllApplications(ArrayList<AppInfo> apps);
-    public void onClickFolderIcon(View v);
-    public void onClickAppShortcut(View v);
-    @Deprecated
-    public void onClickPagedViewIcon(View v);
-    public void onClickWallpaperPicker(View v);
-    public void onClickSettingsButton(View v);
-    public void onClickAddWidgetButton(View v);
-    public void onPageSwitch(View newPage, int newPageIndex);
-    public void onWorkspaceLockedChanged();
-    public void onDragStarted(View view);
     public void onInteractionBegin();
     public void onInteractionEnd();
 
-    public boolean providesSearch();
-    public boolean startSearch(String initialQuery, boolean selectInitialQuery,
-            Bundle appSearchData, Rect sourceBounds);
     @Deprecated
-    public boolean startSearchFromAllApps(String query);
+    public void onWorkspaceLockedChanged();
+
+    /**
+     * Starts a search with {@param initialQuery}. Return false if search was not started.
+     */
+    public boolean startSearch(
+            String initialQuery, boolean selectInitialQuery, Bundle appSearchData);
     public boolean hasCustomContentToLeft();
     public void populateCustomContentContainer();
     public View getQsbBar();
@@ -84,14 +92,9 @@
     /*
      * Extensions points for adding / replacing some other aspects of the Launcher experience.
      */
-    public Intent getFirstRunActivity();
-    public boolean hasFirstRunActivity();
-    public boolean hasDismissableIntroScreen();
-    public View getIntroScreen();
+    public UserEventDispatcher getUserEventDispatcher();
     public boolean shouldMoveToDefaultScreenOnHomeIntent();
     public boolean hasSettings();
-    public boolean overrideWallpaperDimensions();
-    public boolean isLauncherPreinstalled();
     public AllAppsSearchBarController getAllAppsSearchBarController();
     public List<ComponentKey> getPredictedApps();
     public static final int SEARCH_BAR_HEIGHT_NORMAL = 0, SEARCH_BAR_HEIGHT_TALL = 1;
@@ -105,4 +108,6 @@
      *                  but for implementation purposes is passed around as an object.
      */
     public void setLauncherSearchCallback(Object callbacks);
+
+    public boolean shouldShowDiscoveryBounce();
 }
diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java
index 5c1e830..c1282b5 100644
--- a/src/com/android/launcher3/LauncherClings.java
+++ b/src/com/android/launcher3/LauncherClings.java
@@ -16,241 +16,12 @@
 
 package com.android.launcher3;
 
-import android.animation.ObjectAnimator;
-import android.animation.PropertyValuesHolder;
-import android.annotation.TargetApi;
-import android.app.ActivityManager;
 import android.content.Context;
-import android.content.SharedPreferences;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-import android.view.accessibility.AccessibilityManager;
 
-import com.android.launcher3.util.Thunk;
-
-class LauncherClings implements OnClickListener {
-    private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
+@Deprecated
+public class LauncherClings {
     private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
 
-    private static final String TAG_CROP_TOP_AND_SIDES = "crop_bg_top_and_sides";
-
-    private static final int SHOW_CLING_DURATION = 250;
-    private static final int DISMISS_CLING_DURATION = 200;
-
-    // New Secure Setting in L
-    private static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
-
-    @Thunk Launcher mLauncher;
-    private LayoutInflater mInflater;
-    @Thunk boolean mIsVisible;
-
-    /** Ctor */
-    public LauncherClings(Launcher launcher) {
-        mLauncher = launcher;
-        mInflater = LayoutInflater.from(mLauncher);
-    }
-
-    @Override
-    public void onClick(View v) {
-        int id = v.getId();
-        if (id == R.id.cling_dismiss_migration_use_default) {
-            // Disable the migration cling
-            dismissMigrationCling();
-        } else if (id == R.id.cling_dismiss_migration_copy_apps) {
-            // Copy the shortcuts from the old database
-            LauncherModel model = mLauncher.getModel();
-            model.resetLoadedState(false, true);
-            model.startLoader(PagedView.INVALID_RESTORE_PAGE,
-                    LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
-                            | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
-            // Set the flag to skip the folder cling
-            SharedPreferences.Editor editor = Utilities.getPrefs(mLauncher).edit();
-            editor.putBoolean(Launcher.USER_HAS_MIGRATED, true);
-            editor.apply();
-            // Disable the migration cling
-            dismissMigrationCling();
-        } else if (id == R.id.cling_dismiss_longpress_info) {
-            dismissLongPressCling();
-        }
-    }
-
-    /**
-     * Shows the migration cling.
-     *
-     * This flow is mutually exclusive with showFirstRunCling, and only runs if this Launcher
-     * package was not preinstalled and there exists a db to migrate from.
-     */
-    public void showMigrationCling() {
-        mIsVisible = true;
-        mLauncher.hideWorkspaceSearchAndHotseat();
-
-        ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
-        View inflated = mInflater.inflate(R.layout.migration_cling, root);
-        inflated.findViewById(R.id.cling_dismiss_migration_copy_apps).setOnClickListener(this);
-        inflated.findViewById(R.id.cling_dismiss_migration_use_default).setOnClickListener(this);
-    }
-
-    private void dismissMigrationCling() {
-        mLauncher.showWorkspaceSearchAndHotseat();
-        Runnable dismissCb = new Runnable() {
-            public void run() {
-                Runnable cb = new Runnable() {
-                    public void run() {
-                        // Show the longpress cling next
-                        showLongPressCling(false);
-                    }
-                };
-                dismissCling(mLauncher.findViewById(R.id.migration_cling), cb,
-                        MIGRATION_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
-            }
-        };
-        mLauncher.getWorkspace().post(dismissCb);
-    }
-
-    public void showLongPressCling(boolean showWelcome) {
-        mIsVisible = true;
-        ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
-        View cling = mInflater.inflate(R.layout.longpress_cling, root, false);
-
-        cling.setOnLongClickListener(new OnLongClickListener() {
-
-            @Override
-            public boolean onLongClick(View v) {
-                mLauncher.showOverviewMode(true);
-                dismissLongPressCling();
-                return true;
-            }
-        });
-
-        final ViewGroup content = (ViewGroup) cling.findViewById(R.id.cling_content);
-        mInflater.inflate(showWelcome ? R.layout.longpress_cling_welcome_content
-                : R.layout.longpress_cling_content, content);
-        content.findViewById(R.id.cling_dismiss_longpress_info).setOnClickListener(this);
-
-        if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) {
-            Drawable bg = new BorderCropDrawable(mLauncher.getResources().getDrawable(R.drawable.cling_bg),
-                    true, true, true, false);
-            content.setBackground(bg);
-        }
-
-        root.addView(cling);
-
-        if (showWelcome) {
-            // This is the first cling being shown. No need to animate.
-            return;
-        }
-
-        // Animate
-        content.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
-
-            @Override
-            public void onGlobalLayout() {
-                content.getViewTreeObserver().removeOnGlobalLayoutListener(this);
-
-                ObjectAnimator anim;
-                if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) {
-                    content.setTranslationY(-content.getMeasuredHeight());
-                    anim = LauncherAnimUtils.ofFloat(content, "translationY", 0);
-                } else {
-                    content.setScaleX(0);
-                    content.setScaleY(0);
-                    PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1);
-                    PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1);
-                    anim = LauncherAnimUtils.ofPropertyValuesHolder(content, scaleX, scaleY);
-                }
-
-                anim.setDuration(SHOW_CLING_DURATION);
-                anim.setInterpolator(new LogDecelerateInterpolator(100, 0));
-                anim.start();
-            }
-        });
-    }
-
-    @Thunk void dismissLongPressCling() {
-        Runnable dismissCb = new Runnable() {
-            public void run() {
-                dismissCling(mLauncher.findViewById(R.id.longpress_cling), null,
-                        WORKSPACE_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
-            }
-        };
-        mLauncher.getWorkspace().post(dismissCb);
-    }
-
-    /** Hides the specified Cling */
-    @Thunk void dismissCling(final View cling, final Runnable postAnimationCb,
-                              final String flag, int duration) {
-        // To catch cases where siblings of top-level views are made invisible, just check whether
-        // the cling is directly set to GONE before dismissing it.
-        if (cling != null && cling.getVisibility() != View.GONE) {
-            final Runnable cleanUpClingCb = new Runnable() {
-                public void run() {
-                    cling.setVisibility(View.GONE);
-                    mLauncher.getSharedPrefs().edit()
-                        .putBoolean(flag, true)
-                        .apply();
-                    mIsVisible = false;
-                    if (postAnimationCb != null) {
-                        postAnimationCb.run();
-                    }
-                }
-            };
-            if (duration <= 0) {
-                cleanUpClingCb.run();
-            } else {
-                cling.animate().alpha(0).setDuration(duration).withEndAction(cleanUpClingCb);
-            }
-        }
-    }
-
-    public boolean isVisible() {
-        return mIsVisible;
-    }
-
-    /** Returns whether the clings are enabled or should be shown */
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
-    private boolean areClingsEnabled() {
-        // disable clings when running in a test harness
-        if(ActivityManager.isRunningInTestHarness()) return false;
-
-        // Disable clings for accessibility when explore by touch is enabled
-        final AccessibilityManager a11yManager = (AccessibilityManager) mLauncher.getSystemService(
-                Launcher.ACCESSIBILITY_SERVICE);
-        if (a11yManager.isTouchExplorationEnabled()) {
-            return false;
-        }
-
-        // Restricted secondary users (child mode) will potentially have very few apps
-        // seeded when they start up for the first time. Clings won't work well with that
-        if (Utilities.ATLEAST_JB_MR2) {
-            UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
-            Bundle restrictions = um.getUserRestrictions();
-            if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
-                return false;
-            }
-        }
-        if (Settings.Secure.getInt(mLauncher.getContentResolver(), SKIP_FIRST_USE_HINTS, 0)
-                == 1) {
-            return false;
-        }
-        return true;
-    }
-
-    public boolean shouldShowFirstRunOrMigrationClings() {
-        SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
-        return areClingsEnabled() &&
-            !sharedPrefs.getBoolean(WORKSPACE_CLING_DISMISSED_KEY, false) &&
-            !sharedPrefs.getBoolean(MIGRATION_CLING_DISMISSED_KEY, false);
-    }
-
     public static void markFirstRunClingDismissed(Context ctx) {
         Utilities.getPrefs(ctx).edit()
                 .putBoolean(WORKSPACE_CLING_DISMISSED_KEY, true)
diff --git a/src/com/android/launcher3/LauncherExterns.java b/src/com/android/launcher3/LauncherExterns.java
new file mode 100644
index 0000000..887859c
--- /dev/null
+++ b/src/com/android/launcher3/LauncherExterns.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.SharedPreferences;
+
+/**
+ * This interface defines the set of methods that the Launcher activity exposes. Methods
+ * here should be safe to call from classes outside of com.android.launcher3.*
+ */
+public interface LauncherExterns {
+
+    public boolean setLauncherCallbacks(LauncherCallbacks callbacks);
+
+    public SharedPreferences getSharedPrefs();
+
+    public void setLauncherOverlay(Launcher.LauncherOverlay overlay);
+
+    void clearTypedText();
+}
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index 6ce2293..9c4646b 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -14,33 +14,20 @@
 
     private static final String XML = ".xml";
 
-    public static final String DEFAULT_WALLPAPER_THUMBNAIL = "default_thumb2.jpg";
-    public static final String DEFAULT_WALLPAPER_THUMBNAIL_OLD = "default_thumb.jpg";
     public static final String LAUNCHER_DB = "launcher.db";
     public static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
-    public static final String WALLPAPER_CROP_PREFERENCES_KEY =
-            "com.android.launcher3.WallpaperCropActivity";
     public static final String MANAGED_USER_PREFERENCES_KEY = "com.android.launcher3.managedusers.prefs";
+    // This preference file is not backed up to cloud.
+    public static final String DEVICE_PREFERENCES_KEY = "com.android.launcher3.device.prefs";
 
-    public static final String WALLPAPER_IMAGES_DB = "saved_wallpaper_images.db";
     public static final String WIDGET_PREVIEWS_DB = "widgetpreviews.db";
     public static final String APP_ICONS_DB = "app_icons.db";
 
     public static final List<String> ALL_FILES = Collections.unmodifiableList(Arrays.asList(
-            DEFAULT_WALLPAPER_THUMBNAIL,
-            DEFAULT_WALLPAPER_THUMBNAIL_OLD,
             LAUNCHER_DB,
             SHARED_PREFERENCES_KEY + XML,
-            WALLPAPER_CROP_PREFERENCES_KEY + XML,
-            WALLPAPER_IMAGES_DB,
             WIDGET_PREVIEWS_DB,
             MANAGED_USER_PREFERENCES_KEY + XML,
+            DEVICE_PREFERENCES_KEY + XML,
             APP_ICONS_DB));
-
-    // TODO: Delete these files on upgrade
-    public static final List<String> OBSOLETE_FILES = Collections.unmodifiableList(Arrays.asList(
-            "launches.log",
-            "stats.log",
-            "launcher.preferences",
-            "com.android.launcher3.compat.PackageInstallerCompatV16.queue"));
 }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index c8860e3..3ac9773 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3;
 
-import android.app.SearchManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -28,7 +27,6 @@
 import android.content.Intent.ShortcutIconResource;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.database.Cursor;
 import android.graphics.Bitmap;
@@ -39,10 +37,12 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.provider.BaseColumns;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.LongSparseArray;
+import android.util.MutableInt;
 import android.util.Pair;
 
 import com.android.launcher3.compat.AppWidgetManagerCompat;
@@ -52,16 +52,31 @@
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.dynamicui.ExtractionUtils;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.model.GridSizeMigrationTask;
 import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.provider.ImportDataTask;
+import com.android.launcher3.provider.LauncherDbUtils;
+import com.android.launcher3.shortcuts.DeepShortcutManager;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.CursorIconInfo;
 import com.android.launcher3.util.FlagOp;
+import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.util.MultiHashMap;
 import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.StringFilter;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.ViewOnDrawExecutor;
 
 import java.lang.ref.WeakReference;
 import java.net.URISyntaxException;
@@ -74,8 +89,10 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.concurrent.Executor;
 
 /**
  * Maintains in-memory state of the Launcher. It is expected that there should be only one
@@ -86,19 +103,12 @@
         implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
     static final boolean DEBUG_LOADERS = false;
     private static final boolean DEBUG_RECEIVER = false;
-    private static final boolean REMOVE_UNRESTORED_ICONS = true;
 
     static final String TAG = "Launcher.Model";
 
-    public static final int LOADER_FLAG_NONE = 0;
-    public static final int LOADER_FLAG_CLEAR_WORKSPACE = 1 << 0;
-    public static final int LOADER_FLAG_MIGRATE_SHORTCUTS = 1 << 1;
-
     private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
     private static final long INVALID_SCREEN_ID = -1L;
 
-    private final boolean mOldContentProviderExists;
-
     @Thunk final LauncherAppState mApp;
     @Thunk final Object mLock = new Object();
     @Thunk DeferredHandler mHandler = new DeferredHandler();
@@ -106,8 +116,6 @@
     @Thunk boolean mIsLoaderTaskRunning;
     @Thunk boolean mHasLoaderCompletedOnce;
 
-    private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings";
-
     @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
     static {
         sWorkerThread.start();
@@ -117,14 +125,9 @@
     // We start off with everything not loaded.  After that, we assume that
     // our monitoring of the package manager provides all updates and we never
     // need to do a requery.  These are only ever touched from the loader thread.
-    @Thunk boolean mWorkspaceLoaded;
-    @Thunk boolean mAllAppsLoaded;
-
-    // When we are loading pages synchronously, we can't just post the binding of items on the side
-    // pages as this delays the rotation process.  Instead, we wait for a callback from the first
-    // draw (in Workspace) to initiate the binding of the remaining side pages.  Any time we start
-    // a normal load, we also clear this set of Runnables.
-    static final ArrayList<Runnable> mDeferredBindRunnables = new ArrayList<Runnable>();
+    private boolean mWorkspaceLoaded;
+    private boolean mAllAppsLoaded;
+    private boolean mDeepShortcutsLoaded;
 
     /**
      * Set of runnables to be called on the background thread after the workspace binding
@@ -139,6 +142,23 @@
     // Entire list of widgets.
     private final WidgetsModel mBgWidgetsModel;
 
+    // Maps all launcher activities to the id's of their shortcuts (if they have any).
+    private final MultiHashMap<ComponentKey, String> mBgDeepShortcutMap = new MultiHashMap<>();
+
+    private boolean mHasShortcutHostPermission;
+    // Runnable to check if the shortcuts permission has changed.
+    private final Runnable mShortcutPermissionCheckRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (mDeepShortcutsLoaded) {
+                boolean hasShortcutHostPermission = mDeepShortcutManager.hasHostPermission();
+                if (hasShortcutHostPermission != mHasShortcutHostPermission) {
+                    mApp.reloadWorkspace();
+                }
+            }
+        }
+    };
+
     // The lock that must be acquired before referencing any static bg data structures.  Unlike
     // other locks, this one can generally be held long-term because we never expect any of these
     // static data structures to be referenced outside of the worker thread except on the first
@@ -164,26 +184,31 @@
     // sBgWorkspaceScreens is the ordered set of workspace screens.
     static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
 
+    // sBgPinnedShortcutCounts is the ComponentKey representing a pinned shortcut to the number of
+    // times it is pinned.
+    static final Map<ShortcutKey, MutableInt> sBgPinnedShortcutCounts = new HashMap<>();
+
     // sPendingPackages is a set of packages which could be on sdcard and are not available yet
     static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
             new HashMap<UserHandleCompat, HashSet<String>>();
 
     // </ only access in worker thread >
 
-    @Thunk IconCache mIconCache;
+    private IconCache mIconCache;
+    private DeepShortcutManager mDeepShortcutManager;
 
-    @Thunk final LauncherAppsCompat mLauncherApps;
-    @Thunk final UserManagerCompat mUserManager;
+    private final LauncherAppsCompat mLauncherApps;
+    private final UserManagerCompat mUserManager;
 
     public interface Callbacks {
         public boolean setLoadOnResume();
         public int getCurrentWorkspaceScreen();
+        public void clearPendingBinds();
         public void startBinding();
         public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end,
                               boolean forceAnimateIcons);
         public void bindScreens(ArrayList<Long> orderedScreenIds);
-        public void bindAddScreens(ArrayList<Long> orderedScreenIds);
-        public void bindFolders(LongArrayMap<FolderInfo> folders);
+        public void finishFirstPageBind(ViewOnDrawExecutor executor);
         public void finishBindingItems();
         public void bindAppWidget(LauncherAppWidgetInfo info);
         public void bindAllApplications(ArrayList<AppInfo> apps);
@@ -202,41 +227,23 @@
         public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
         public void notifyWidgetProvidersChanged();
         public void bindWidgetsModel(WidgetsModel model);
-        public void bindSearchProviderChanged();
-        public boolean isAllAppsButtonRank(int rank);
         public void onPageBoundSynchronously(int page);
-        public void dumpLogsToLocalData();
+        public void executeOnNextDraw(ViewOnDrawExecutor executor);
+        public void bindDeepShortcutMap(MultiHashMap<ComponentKey, String> deepShortcutMap);
     }
 
     public interface ItemInfoFilter {
         public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn);
     }
 
-    LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
+    LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter,
+            DeepShortcutManager deepShortcutManager) {
         Context context = app.getContext();
-
-        String oldProvider = context.getString(R.string.old_launcher_provider_uri);
-        // This may be the same as MIGRATE_AUTHORITY, or it may be replaced by a different
-        // resource string.
-        String redirectAuthority = Uri.parse(oldProvider).getAuthority();
-        ProviderInfo providerInfo =
-                context.getPackageManager().resolveContentProvider(MIGRATE_AUTHORITY, 0);
-        ProviderInfo redirectProvider =
-                context.getPackageManager().resolveContentProvider(redirectAuthority, 0);
-
-        Log.d(TAG, "Old launcher provider: " + oldProvider);
-        mOldContentProviderExists = (providerInfo != null) && (redirectProvider != null);
-
-        if (mOldContentProviderExists) {
-            Log.d(TAG, "Old launcher provider exists.");
-        } else {
-            Log.d(TAG, "Old launcher provider does not exist.");
-        }
-
         mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
         mBgWidgetsModel = new WidgetsModel(context, iconCache, appFilter);
         mIconCache = iconCache;
+        mDeepShortcutManager = deepShortcutManager;
 
         mLauncherApps = LauncherAppsCompat.getInstance(context);
         mUserManager = UserManagerCompat.getInstance(context);
@@ -244,7 +251,7 @@
 
     /** Runs the specified runnable immediately if called from the main thread, otherwise it is
      * posted on the main thread handler. */
-    @Thunk void runOnMainThread(Runnable r) {
+    private void runOnMainThread(Runnable r) {
         if (sWorkerThread.getThreadId() == Process.myTid()) {
             // If we are on the worker thread, post onto the main handler
             mHandler.post(r);
@@ -255,7 +262,7 @@
 
     /** Runs the specified runnable immediately if called from the worker thread, otherwise it is
      * posted on the worker thread handler. */
-    @Thunk static void runOnWorkerThread(Runnable r) {
+    private static void runOnWorkerThread(Runnable r) {
         if (sWorkerThread.getThreadId() == Process.myTid()) {
             r.run();
         } else {
@@ -264,10 +271,6 @@
         }
     }
 
-    boolean canMigrateFromOldLauncherDb(Launcher launcher) {
-        return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ;
-    }
-
     public void setPackageState(final PackageInstallInfo installInfo) {
         Runnable updateRunnable = new Runnable() {
 
@@ -332,8 +335,8 @@
             @Override
             public void run() {
                 synchronized (sBgLock) {
-                    final ArrayList<ShortcutInfo> updates = new ArrayList<>();
-                    final UserHandleCompat user = UserHandleCompat.myUserHandle();
+                    ArrayList<ShortcutInfo> updates = new ArrayList<>();
+                    UserHandleCompat user = UserHandleCompat.myUserHandle();
 
                     for (ItemInfo info : sBgItemsIdMap) {
                         if (info instanceof ShortcutInfo) {
@@ -355,19 +358,7 @@
                         }
                     }
 
-                    if (!updates.isEmpty()) {
-                        // Push changes to the callback.
-                        Runnable r = new Runnable() {
-                            public void run() {
-                                Callbacks callbacks = getCallback();
-                                if (callbacks != null) {
-                                    callbacks.bindShortcutsChanged(updates,
-                                            new ArrayList<ShortcutInfo>(), user);
-                                }
-                            }
-                        };
-                        mHandler.post(r);
-                    }
+                    bindUpdatedShortcuts(updates, user);
                 }
             }
         };
@@ -404,21 +395,14 @@
             int[] xy, int spanX, int spanY) {
         LauncherAppState app = LauncherAppState.getInstance();
         InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
-        final int xCount = (int) profile.numColumns;
-        final int yCount = (int) profile.numRows;
-        boolean[][] occupied = new boolean[xCount][yCount];
+
+        GridOccupancy occupied = new GridOccupancy(profile.numColumns, profile.numRows);
         if (occupiedPos != null) {
             for (ItemInfo r : occupiedPos) {
-                int right = r.cellX + r.spanX;
-                int bottom = r.cellY + r.spanY;
-                for (int x = r.cellX; 0 <= x && x < right && x < xCount; x++) {
-                    for (int y = r.cellY; 0 <= y && y < bottom && y < yCount; y++) {
-                        occupied[x][y] = true;
-                    }
-                }
+                occupied.markCells(r, true);
             }
         }
-        return Utilities.findVacantCell(xy, spanX, spanY, xCount, yCount, occupied);
+        return occupied.findVacantCell(xy, spanX, spanY);
     }
 
     /**
@@ -476,7 +460,9 @@
 
         if (!found) {
             // Still no position found. Add a new screen to the end.
-            screenId = LauncherAppState.getLauncherProvider().generateNewScreenId();
+            screenId = LauncherSettings.Settings.call(context.getContentResolver(),
+                    LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
+                    .getLong(LauncherSettings.Settings.EXTRA_VALUE);
 
             // Save the screen id for binding in the workspace
             workspaceScreens.add(screenId);
@@ -521,8 +507,7 @@
 
                         // Find appropriate space for the item.
                         Pair<Long, int[]> coords = findSpaceForItem(context,
-                                workspaceScreens, addedWorkspaceScreensFinal,
-                                1, 1);
+                                workspaceScreens, addedWorkspaceScreensFinal, 1, 1);
                         long screenId = coords.first;
                         int[] cordinates = coords.second;
 
@@ -576,48 +561,11 @@
         runOnWorkerThread(r);
     }
 
-    private void unbindItemInfosAndClearQueuedBindRunnables() {
-        if (sWorkerThread.getThreadId() == Process.myTid()) {
-            throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " +
-                    "main thread");
-        }
-
-        // Clear any deferred bind runnables
-        synchronized (mDeferredBindRunnables) {
-            mDeferredBindRunnables.clear();
-        }
-
-        // Remove any queued UI runnables
-        mHandler.cancelAll();
-        // Unbind all the workspace items
-        unbindWorkspaceItemsOnMainThread();
-    }
-
-    /** Unbinds all the sBgWorkspaceItems and sBgAppWidgets on the main thread */
-    void unbindWorkspaceItemsOnMainThread() {
-        // Ensure that we don't use the same workspace items data structure on the main thread
-        // by making a copy of workspace items first.
-        final ArrayList<ItemInfo> tmpItems = new ArrayList<ItemInfo>();
-        synchronized (sBgLock) {
-            tmpItems.addAll(sBgWorkspaceItems);
-            tmpItems.addAll(sBgAppWidgets);
-        }
-        Runnable r = new Runnable() {
-                @Override
-                public void run() {
-                   for (ItemInfo item : tmpItems) {
-                       item.unbind();
-                   }
-                }
-            };
-        runOnMainThread(r);
-    }
-
     /**
      * Adds an item to the DB if it was not created previously, or move it to a new
      * <container, screen, cellX, cellY>
      */
-    static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
+    public static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
             long screenId, int cellX, int cellY) {
         if (item.container == ItemInfo.NO_ID) {
             // From all apps
@@ -645,12 +593,7 @@
                         modelShortcut.cellX == shortcut.cellX &&
                         modelShortcut.cellY == shortcut.cellY &&
                         modelShortcut.spanX == shortcut.spanX &&
-                        modelShortcut.spanY == shortcut.spanY &&
-                        ((modelShortcut.dropPos == null && shortcut.dropPos == null) ||
-                        (modelShortcut.dropPos != null &&
-                                shortcut.dropPos != null &&
-                                modelShortcut.dropPos[0] == shortcut.dropPos[0] &&
-                        modelShortcut.dropPos[1] == shortcut.dropPos[1]))) {
+                        modelShortcut.spanY == shortcut.spanY) {
                     // For all intents and purposes, this is the same object
                     return;
                 }
@@ -757,6 +700,7 @@
                 switch (modelItem.itemType) {
                     case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                     case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                    case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                     case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                         if (!sBgWorkspaceItems.contains(modelItem)) {
                             sBgWorkspaceItems.add(modelItem);
@@ -784,7 +728,8 @@
         // in the hotseat
         if (context instanceof Launcher && screenId < 0 &&
                 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
+            item.screenId = Launcher.getLauncher(context).getHotseat()
+                    .getOrderInHotseat(cellX, cellY);
         } else {
             item.screenId = screenId;
         }
@@ -803,7 +748,7 @@
      * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
      * cellX, cellY have already been updated on the ItemInfos.
      */
-    static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items,
+    public static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items,
             final long container, final int screen) {
 
         ArrayList<ContentValues> contentValues = new ArrayList<ContentValues>();
@@ -817,7 +762,7 @@
             // in the hotseat
             if (context instanceof Launcher && screen < 0 &&
                     container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-                item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(item.cellX,
+                item.screenId = Launcher.getLauncher(context).getHotseat().getOrderInHotseat(item.cellX,
                         item.cellY);
             } else {
                 item.screenId = screen;
@@ -850,7 +795,8 @@
         // in the hotseat
         if (context instanceof Launcher && screenId < 0 &&
                 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
+            item.screenId = Launcher.getLauncher(context).getHotseat()
+                    .getOrderInHotseat(cellX, cellY);
         } else {
             item.screenId = screenId;
         }
@@ -877,7 +823,7 @@
     }
 
     private void assertWorkspaceLoaded() {
-        if (LauncherAppState.isDogfoodBuild()) {
+        if (ProviderConfig.IS_DOGFOOD_BUILD) {
             synchronized (mLock) {
                 if (!mHasLoaderCompletedOnce ||
                         (mLoaderTask != null && mLoaderTask.mIsLoadingAndBindingWorkspace)) {
@@ -917,7 +863,9 @@
                     Intent targetIntent = info.promisedIntent == null
                             ? info.intent : info.promisedIntent;
                     if (targetIntent != null && info.user.equals(user)) {
-                        String s = targetIntent.toUri(0);
+                        Intent copyIntent = new Intent(targetIntent);
+                        copyIntent.setSourceBounds(intent.getSourceBounds());
+                        String s = copyIntent.toUri(0);
                         if (intentWithPkg.equals(s) || intentWithoutPkg.equals(s)) {
                             return true;
                         }
@@ -929,51 +877,6 @@
     }
 
     /**
-     * Find a folder in the db, creating the FolderInfo if necessary, and adding it to folderList.
-     */
-    FolderInfo getFolderById(Context context, LongArrayMap<FolderInfo> folderList, long id) {
-        final ContentResolver cr = context.getContentResolver();
-        Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, null,
-                "_id=? and (itemType=? or itemType=?)",
-                new String[] { String.valueOf(id),
-                        String.valueOf(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)}, null);
-
-        try {
-            if (c.moveToFirst()) {
-                final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
-                final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
-                final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
-                final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
-                final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
-                final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
-                final int optionsIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.OPTIONS);
-
-                FolderInfo folderInfo = null;
-                switch (c.getInt(itemTypeIndex)) {
-                    case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
-                        folderInfo = findOrMakeFolder(folderList, id);
-                        break;
-                }
-
-                // Do not trim the folder label, as is was set by the user.
-                folderInfo.title = c.getString(titleIndex);
-                folderInfo.id = id;
-                folderInfo.container = c.getInt(containerIndex);
-                folderInfo.screenId = c.getInt(screenIndex);
-                folderInfo.cellX = c.getInt(cellXIndex);
-                folderInfo.cellY = c.getInt(cellYIndex);
-                folderInfo.options = c.getInt(optionsIndex);
-
-                return folderInfo;
-            }
-        } finally {
-            c.close();
-        }
-
-        return null;
-    }
-
-    /**
      * Add an item to the database in a specified container. Sets the container, screen, cellX and
      * cellY fields of the item. Also assigns an ID to the item.
      */
@@ -986,7 +889,8 @@
         // in the hotseat
         if (context instanceof Launcher && screenId < 0 &&
                 container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-            item.screenId = ((Launcher) context).getHotseat().getOrderInHotseat(cellX, cellY);
+            item.screenId = Launcher.getLauncher(context).getHotseat()
+                    .getOrderInHotseat(cellX, cellY);
         } else {
             item.screenId = screenId;
         }
@@ -995,7 +899,9 @@
         final ContentResolver cr = context.getContentResolver();
         item.onAddToDatabase(context, values);
 
-        item.id = LauncherAppState.getLauncherProvider().generateNewItemId();
+        item.id = LauncherSettings.Settings.call(cr, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
+                .getLong(LauncherSettings.Settings.EXTRA_VALUE);
+
         values.put(LauncherSettings.Favorites._ID, item.id);
 
         final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
@@ -1013,6 +919,7 @@
                             // Fall through
                         case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                         case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                        case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                             if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
                                     item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
                                 sBgWorkspaceItems.add(item);
@@ -1024,6 +931,11 @@
                                     Log.e(TAG, msg);
                                 }
                             }
+                            if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                                incrementPinnedShortcutCount(
+                                        ShortcutKey.fromShortcutInfo((ShortcutInfo) item),
+                                        true /* shouldPin */);
+                            }
                             break;
                         case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                             sBgAppWidgets.add((LauncherAppWidgetInfo) item);
@@ -1035,15 +947,6 @@
         runOnWorkerThread(r);
     }
 
-    /**
-     * Creates a new unique child id, for a given cell span across all layouts.
-     */
-    static int getCellLayoutChildId(
-            long container, long screen, int localCellX, int localCellY, int spanX, int spanY) {
-        return (((int) container & 0xFF) << 24)
-                | ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
-    }
-
     private static ArrayList<ItemInfo> getItemsByPackageName(
             final String pn, final UserHandleCompat user) {
         ItemInfoFilter filter  = new ItemInfoFilter() {
@@ -1099,6 +1002,10 @@
                                 }
                                 sBgWorkspaceItems.remove(item);
                                 break;
+                            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+                                decrementPinnedShortcutCount(ShortcutKey.fromShortcutInfo(
+                                        (ShortcutInfo) item));
+                                // Fall through.
                             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                                 sBgWorkspaceItems.remove(item);
@@ -1116,6 +1023,39 @@
     }
 
     /**
+     * Decrement the count for the given pinned shortcut, unpinning it if the count becomes 0.
+     */
+    private static void decrementPinnedShortcutCount(final ShortcutKey pinnedShortcut) {
+        synchronized (sBgLock) {
+            MutableInt count = sBgPinnedShortcutCounts.get(pinnedShortcut);
+            if (count == null || --count.value == 0) {
+                LauncherAppState.getInstance().getShortcutManager().unpinShortcut(pinnedShortcut);
+            }
+        }
+    }
+
+    /**
+     * Increment the count for the given shortcut, pinning it if the count becomes 1.
+     *
+     * As an optimization, the caller can pass shouldPin == false to avoid
+     * unnecessary RPC's if the shortcut is already pinned.
+     */
+    private static void incrementPinnedShortcutCount(ShortcutKey pinnedShortcut, boolean shouldPin) {
+        synchronized (sBgLock) {
+            MutableInt count = sBgPinnedShortcutCounts.get(pinnedShortcut);
+            if (count == null) {
+                count = new MutableInt(1);
+                sBgPinnedShortcutCounts.put(pinnedShortcut, count);
+            } else {
+                count.value++;
+            }
+            if (shouldPin && count.value == 1) {
+                LauncherAppState.getInstance().getShortcutManager().pinShortcut(pinnedShortcut);
+            }
+        }
+    }
+
+    /**
      * Update the order of the workspace screens in the database. The array list contains
      * a list of screen ids in the order that they should appear.
      */
@@ -1197,38 +1137,38 @@
      */
     public void initialize(Callbacks callbacks) {
         synchronized (mLock) {
-            // Disconnect any of the callbacks and drawables associated with ItemInfos on the
-            // workspace to prevent leaking Launcher activities on orientation change.
-            unbindItemInfosAndClearQueuedBindRunnables();
-            mCallbacks = new WeakReference<Callbacks>(callbacks);
+            Preconditions.assertUIThread();
+            // Remove any queued UI runnables
+            mHandler.cancelAll();
+            mCallbacks = new WeakReference<>(callbacks);
         }
     }
 
     @Override
     public void onPackageChanged(String packageName, UserHandleCompat user) {
         int op = PackageUpdatedTask.OP_UPDATE;
-        enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
+        enqueueItemUpdatedTask(new PackageUpdatedTask(op, new String[] { packageName },
                 user));
     }
 
     @Override
     public void onPackageRemoved(String packageName, UserHandleCompat user) {
         int op = PackageUpdatedTask.OP_REMOVE;
-        enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
+        enqueueItemUpdatedTask(new PackageUpdatedTask(op, new String[] { packageName },
                 user));
     }
 
     @Override
     public void onPackageAdded(String packageName, UserHandleCompat user) {
         int op = PackageUpdatedTask.OP_ADD;
-        enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
+        enqueueItemUpdatedTask(new PackageUpdatedTask(op, new String[] { packageName },
                 user));
     }
 
     @Override
     public void onPackagesAvailable(String[] packageNames, UserHandleCompat user,
             boolean replacing) {
-        enqueuePackageUpdated(
+        enqueueItemUpdatedTask(
                 new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, packageNames, user));
     }
 
@@ -1236,7 +1176,7 @@
     public void onPackagesUnavailable(String[] packageNames, UserHandleCompat user,
             boolean replacing) {
         if (!replacing) {
-            enqueuePackageUpdated(new PackageUpdatedTask(
+            enqueueItemUpdatedTask(new PackageUpdatedTask(
                     PackageUpdatedTask.OP_UNAVAILABLE, packageNames,
                     user));
         }
@@ -1244,18 +1184,29 @@
 
     @Override
     public void onPackagesSuspended(String[] packageNames, UserHandleCompat user) {
-        enqueuePackageUpdated(new PackageUpdatedTask(
+        enqueueItemUpdatedTask(new PackageUpdatedTask(
                 PackageUpdatedTask.OP_SUSPEND, packageNames,
                 user));
     }
 
     @Override
     public void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user) {
-        enqueuePackageUpdated(new PackageUpdatedTask(
+        enqueueItemUpdatedTask(new PackageUpdatedTask(
                 PackageUpdatedTask.OP_UNSUSPEND, packageNames,
                 user));
     }
 
+    @Override
+    public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
+            UserHandleCompat user) {
+        enqueueItemUpdatedTask(new ShortcutsChangedTask(packageName, shortcuts, user, true));
+    }
+
+    public void updatePinnedShortcuts(String packageName, List<ShortcutInfoCompat> shortcuts,
+            UserHandleCompat user) {
+        enqueueItemUpdatedTask(new ShortcutsChangedTask(packageName, shortcuts, user, false));
+    }
+
     /**
      * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
      * ACTION_PACKAGE_CHANGED.
@@ -1268,23 +1219,31 @@
         if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
             // If we have changed locale we need to clear out the labels in all apps/workspace.
             forceReload();
-        } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action)) {
-            Callbacks callbacks = getCallback();
-            if (callbacks != null) {
-                callbacks.bindSearchProviderChanged();
-            }
-        } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action)
-                || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
+        } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)
+                || Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
             UserManagerCompat.getInstance(context).enableAndResetCache();
             forceReload();
-        } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
-                LauncherAppsCompat.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
+        } else if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
+                Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
+                Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
             UserHandleCompat user = UserHandleCompat.fromIntent(intent);
             if (user != null) {
-                enqueuePackageUpdated(new PackageUpdatedTask(
-                        PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE,
-                        new String[0], user));
+                if (Intent.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
+                        Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
+                    enqueueItemUpdatedTask(new PackageUpdatedTask(
+                            PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE,
+                            new String[0], user));
+                }
+
+                // ACTION_MANAGED_PROFILE_UNAVAILABLE sends the profile back to locked mode, so
+                // we need to run the state change task again.
+                if (Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action) ||
+                        Intent.ACTION_MANAGED_PROFILE_UNLOCKED.equals(action)) {
+                    enqueueItemUpdatedTask(new UserLockStateChangedTask(user));
+                }
             }
+        } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(action)) {
+            ExtractionUtils.startColorExtractionServiceIfNecessary(context);
         }
     }
 
@@ -1304,6 +1263,9 @@
             stopLoaderLocked();
             if (resetAllAppsLoaded) mAllAppsLoaded = false;
             if (resetWorkspaceLoaded) mWorkspaceLoaded = false;
+            // Always reset deep shortcuts loaded.
+            // TODO: why?
+            mDeepShortcutsLoaded = false;
         }
     }
 
@@ -1314,17 +1276,13 @@
      * of doing it now.
      */
     public void startLoaderFromBackground() {
-        boolean runLoader = false;
         Callbacks callbacks = getCallback();
         if (callbacks != null) {
             // Only actually run the loader if they're not paused.
             if (!callbacks.setLoadOnResume()) {
-                runLoader = true;
+                startLoader(callbacks.getCurrentWorkspaceScreen());
             }
         }
-        if (runLoader) {
-            startLoader(PagedView.INVALID_RESTORE_PAGE);
-        }
     }
 
     /**
@@ -1341,49 +1299,39 @@
         return (mCallbacks != null && mCallbacks.get() == callbacks);
     }
 
-    public void startLoader(int synchronousBindPage) {
-        startLoader(synchronousBindPage, LOADER_FLAG_NONE);
-    }
-
-    public void startLoader(int synchronousBindPage, int loadFlags) {
+    /**
+     * Starts the loader. Tries to bind {@params synchronousBindPage} synchronously if possible.
+     * @return true if the page could be bound synchronously.
+     */
+    public boolean startLoader(int synchronousBindPage) {
         // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
         InstallShortcutReceiver.enableInstallQueue();
         synchronized (mLock) {
-            // Clear any deferred bind-runnables from the synchronized load process
-            // We must do this before any loading/binding is scheduled below.
-            synchronized (mDeferredBindRunnables) {
-                mDeferredBindRunnables.clear();
-            }
-
             // Don't bother to start the thread if we know it's not going to do anything
             if (mCallbacks != null && mCallbacks.get() != null) {
+                final Callbacks oldCallbacks = mCallbacks.get();
+                // Clear any pending bind-runnables from the synchronized load process.
+                runOnMainThread(new Runnable() {
+                    public void run() {
+                        oldCallbacks.clearPendingBinds();
+                    }
+                });
+
                 // If there is already one running, tell it to stop.
                 stopLoaderLocked();
-                mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags);
-                if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
-                        && mAllAppsLoaded && mWorkspaceLoaded && !mIsLoaderTaskRunning) {
+                mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage);
+                // TODO: mDeepShortcutsLoaded does not need to be true for synchronous bind.
+                if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE && mAllAppsLoaded
+                        && mWorkspaceLoaded && mDeepShortcutsLoaded && !mIsLoaderTaskRunning) {
                     mLoaderTask.runBindSynchronousPage(synchronousBindPage);
+                    return true;
                 } else {
                     sWorkerThread.setPriority(Thread.NORM_PRIORITY);
                     sWorker.post(mLoaderTask);
                 }
             }
         }
-    }
-
-    void bindRemainingSynchronousPages() {
-        // Post the remaining side pages to be loaded
-        if (!mDeferredBindRunnables.isEmpty()) {
-            Runnable[] deferredBindRunnables = null;
-            synchronized (mDeferredBindRunnables) {
-                deferredBindRunnables = mDeferredBindRunnables.toArray(
-                        new Runnable[mDeferredBindRunnables.size()]);
-                mDeferredBindRunnables.clear();
-            }
-            for (final Runnable r : deferredBindRunnables) {
-                mHandler.post(r);
-            }
-        }
+        return false;
     }
 
     public void stopLoader() {
@@ -1402,27 +1350,8 @@
         final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
 
         // Get screens ordered by rank.
-        final Cursor sc = contentResolver.query(screensUri, null, null, null,
-                LauncherSettings.WorkspaceScreens.SCREEN_RANK);
-        ArrayList<Long> screenIds = new ArrayList<Long>();
-        try {
-            final int idIndex = sc.getColumnIndexOrThrow(LauncherSettings.WorkspaceScreens._ID);
-            while (sc.moveToNext()) {
-                try {
-                    screenIds.add(sc.getLong(idIndex));
-                } catch (Exception e) {
-                    Launcher.addDumpLog(TAG, "Desktop items loading interrupted"
-                            + " - invalid screens: " + e, true);
-                }
-            }
-        } finally {
-            sc.close();
-        }
-        return screenIds;
-    }
-
-    public boolean isAllAppsLoaded() {
-        return mAllAppsLoaded;
+        return LauncherDbUtils.getScreenIdsFromCursor(contentResolver.query(
+                screensUri, null, null, null, LauncherSettings.WorkspaceScreens.SCREEN_RANK));
     }
 
     /**
@@ -1430,17 +1359,19 @@
      *   - workspace icons
      *   - widgets
      *   - all apps icons
+     *   - deep shortcuts within apps
      */
     private class LoaderTask implements Runnable {
         private Context mContext;
+        private int mPageToBindFirst;
+
         @Thunk boolean mIsLoadingAndBindingWorkspace;
         private boolean mStopped;
         @Thunk boolean mLoadAndBindStepFinished;
-        private int mFlags;
 
-        LoaderTask(Context context, int flags) {
+        LoaderTask(Context context, int pageToBindFirst) {
             mContext = context;
-            mFlags = flags;
+            mPageToBindFirst = pageToBindFirst;
         }
 
         private void loadAndBindWorkspace() {
@@ -1462,7 +1393,7 @@
             }
 
             // Bind the workspace
-            bindWorkspace(-1);
+            bindWorkspace(mPageToBindFirst);
         }
 
         private void waitForIdle() {
@@ -1535,6 +1466,8 @@
             // XXX: For now, continue posting the binding of AllApps as there are other issues that
             //      arise from that.
             onlyBindAllApps();
+
+            bindDeepShortcuts();
         }
 
         public void run() {
@@ -1560,6 +1493,12 @@
                 // second step
                 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
                 loadAndBindAllApps();
+
+                waitForIdle();
+
+                // third step
+                if (DEBUG_LOADERS) Log.d(TAG, "step 3: loading deep shortcuts");
+                loadAndBindDeepShortcuts();
             }
 
             // Clear out this reference, otherwise we end up holding it until all of the
@@ -1614,25 +1553,23 @@
         }
 
         // check & update map of what's occupied; used to discard overlapping/invalid items
-        private boolean checkItemPlacement(LongArrayMap<ItemInfo[][]> occupied, ItemInfo item,
+        private boolean checkItemPlacement(LongArrayMap<GridOccupancy> occupied, ItemInfo item,
                    ArrayList<Long> workspaceScreens) {
             LauncherAppState app = LauncherAppState.getInstance();
             InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
-            final int countX = profile.numColumns;
-            final int countY = profile.numRows;
 
             long containerIndex = item.screenId;
             if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
                 // Return early if we detect that an item is under the hotseat button
-                if (mCallbacks == null ||
-                        mCallbacks.get().isAllAppsButtonRank((int) item.screenId)) {
+                if (!FeatureFlags.NO_ALL_APPS_ICON &&
+                        profile.isAllAppsButtonRank((int) item.screenId)) {
                     Log.e(TAG, "Error loading shortcut into hotseat " + item
                             + " into position (" + item.screenId + ":" + item.cellX + ","
                             + item.cellY + ") occupied by all apps");
                     return false;
                 }
 
-                final ItemInfo[][] hotseatItems =
+                final GridOccupancy hotseatOccupancy =
                         occupied.get((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT);
 
                 if (item.screenId >= profile.numHotseatIcons) {
@@ -1643,22 +1580,20 @@
                     return false;
                 }
 
-                if (hotseatItems != null) {
-                    if (hotseatItems[(int) item.screenId][0] != null) {
+                if (hotseatOccupancy != null) {
+                    if (hotseatOccupancy.cells[(int) item.screenId][0]) {
                         Log.e(TAG, "Error loading shortcut into hotseat " + item
                                 + " into position (" + item.screenId + ":" + item.cellX + ","
-                                + item.cellY + ") occupied by "
-                                + occupied.get(LauncherSettings.Favorites.CONTAINER_HOTSEAT)
-                                [(int) item.screenId][0]);
+                                + item.cellY + ") already occupied");
                             return false;
                     } else {
-                        hotseatItems[(int) item.screenId][0] = item;
+                        hotseatOccupancy.cells[(int) item.screenId][0] = true;
                         return true;
                     }
                 } else {
-                    final ItemInfo[][] items = new ItemInfo[(int) profile.numHotseatIcons][1];
-                    items[(int) item.screenId][0] = item;
-                    occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
+                    final GridOccupancy occupancy = new GridOccupancy(profile.numHotseatIcons, 1);
+                    occupancy.cells[(int) item.screenId][0] = true;
+                    occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, occupancy);
                     return true;
                 }
             } else if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
@@ -1671,12 +1606,8 @@
                 return true;
             }
 
-            if (!occupied.containsKey(item.screenId)) {
-                ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1];
-                occupied.put(item.screenId, items);
-            }
-
-            final ItemInfo[][] screens = occupied.get(item.screenId);
+            final int countX = profile.numColumns;
+            final int countY = profile.numRows;
             if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
                     item.cellX < 0 || item.cellY < 0 ||
                     item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
@@ -1687,26 +1618,28 @@
                 return false;
             }
 
-            // Check if any workspace icons overlap with each other
-            for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
-                for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
-                    if (screens[x][y] != null) {
-                        Log.e(TAG, "Error loading shortcut " + item
-                            + " into cell (" + containerIndex + "-" + item.screenId + ":"
-                            + x + "," + y
-                            + ") occupied by "
-                            + screens[x][y]);
-                        return false;
-                    }
+            if (!occupied.containsKey(item.screenId)) {
+                GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1);
+                if (item.screenId == Workspace.FIRST_SCREEN_ID) {
+                    // Mark the first row as occupied (if the feature is enabled)
+                    // in order to account for the QSB.
+                    screen.markCells(0, 0, countX + 1, 1, FeatureFlags.QSB_ON_FIRST_SCREEN);
                 }
+                occupied.put(item.screenId, screen);
             }
-            for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
-                for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
-                    screens[x][y] = item;
-                }
-            }
+            final GridOccupancy occupancy = occupied.get(item.screenId);
 
-            return true;
+            // Check if any workspace icons overlap with each other
+            if (occupancy.isRegionVacant(item.cellX, item.cellY, item.spanX, item.spanY)) {
+                occupancy.markCells(item, true);
+                return true;
+            } else {
+                Log.e(TAG, "Error loading shortcut " + item
+                        + " into cell (" + containerIndex + "-" + item.screenId + ":"
+                        + item.cellX + "," + item.cellX + "," + item.spanX + "," + item.spanY
+                        + ") already occupied");
+                return false;
+            }
         }
 
         /** Clears all the sBg data structures */
@@ -1717,10 +1650,14 @@
                 sBgFolders.clear();
                 sBgItemsIdMap.clear();
                 sBgWorkspaceScreens.clear();
+                sBgPinnedShortcutCounts.clear();
             }
         }
 
         private void loadWorkspace() {
+            if (LauncherAppState.PROFILE_STARTUP) {
+                Trace.beginSection("Loading Workspace");
+            }
             final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
 
             final Context context = mContext;
@@ -1728,34 +1665,36 @@
             final PackageManager manager = context.getPackageManager();
             final boolean isSafeMode = manager.isSafeMode();
             final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
-            final boolean isSdCardReady = context.registerReceiver(null,
-                    new IntentFilter(StartupReceiver.SYSTEM_READY)) != null;
+            final boolean isSdCardReady = Utilities.isBootCompleted();
 
             LauncherAppState app = LauncherAppState.getInstance();
             InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
             int countX = profile.numColumns;
             int countY = profile.numRows;
 
-            if (GridSizeMigrationTask.ENABLED &&
+            boolean clearDb = false;
+            try {
+                ImportDataTask.performImportIfPossible(context);
+            } catch (Exception e) {
+                // Migration failed. Clear workspace.
+                clearDb = true;
+            }
+
+            if (!clearDb && GridSizeMigrationTask.ENABLED &&
                     !GridSizeMigrationTask.migrateGridIfNeeded(mContext)) {
                 // Migration failed. Clear workspace.
-                mFlags = mFlags | LOADER_FLAG_CLEAR_WORKSPACE;
+                clearDb = true;
             }
 
-            if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
-                Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
-                LauncherAppState.getLauncherProvider().deleteDatabase();
+            if (clearDb) {
+                Log.d(TAG, "loadWorkspace: resetting launcher database");
+                LauncherSettings.Settings.call(contentResolver,
+                        LauncherSettings.Settings.METHOD_DELETE_DB);
             }
 
-            if ((mFlags & LOADER_FLAG_MIGRATE_SHORTCUTS) != 0) {
-                // append the user's Launcher2 shortcuts
-                Launcher.addDumpLog(TAG, "loadWorkspace: migrating from launcher2", true);
-                LauncherAppState.getLauncherProvider().migrateLauncher2Shortcuts();
-            } else {
-                // Make sure the default workspace is loaded
-                Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false);
-                LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
-            }
+            Log.d(TAG, "loadWorkspace: loading default favorites");
+            LauncherSettings.Settings.call(contentResolver,
+                    LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);
 
             synchronized (sBgLock) {
                 clearSBgDataStructures();
@@ -1765,6 +1704,7 @@
 
                 final ArrayList<Long> itemsToRemove = new ArrayList<>();
                 final ArrayList<Long> restoredRows = new ArrayList<>();
+                Map<ShortcutKey, ShortcutInfoCompat> shortcutKeyToPinnedShortcuts = new HashMap<>();
                 final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
                 if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
                 final Cursor c = contentResolver.query(contentUri, null, null, null, null);
@@ -1772,15 +1712,13 @@
                 // +1 for the hotseat (it can be larger than the workspace)
                 // Load workspace in reverse order to ensure that latest items are loaded first (and
                 // before any earlier duplicates)
-                final LongArrayMap<ItemInfo[][]> occupied = new LongArrayMap<>();
+                final LongArrayMap<GridOccupancy> occupied = new LongArrayMap<>();
                 HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
 
                 try {
                     final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
                     final int intentIndex = c.getColumnIndexOrThrow
                             (LauncherSettings.Favorites.INTENT);
-                    final int titleIndex = c.getColumnIndexOrThrow
-                            (LauncherSettings.Favorites.TITLE);
                     final int containerIndex = c.getColumnIndexOrThrow(
                             LauncherSettings.Favorites.CONTAINER);
                     final int itemTypeIndex = c.getColumnIndexOrThrow(
@@ -1807,14 +1745,35 @@
                             LauncherSettings.Favorites.PROFILE_ID);
                     final int optionsIndex = c.getColumnIndexOrThrow(
                             LauncherSettings.Favorites.OPTIONS);
-                    final CursorIconInfo cursorIconInfo = new CursorIconInfo(c);
+                    final CursorIconInfo cursorIconInfo = new CursorIconInfo(mContext, c);
 
                     final LongSparseArray<UserHandleCompat> allUsers = new LongSparseArray<>();
                     final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
+                    final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
                     for (UserHandleCompat user : mUserManager.getUserProfiles()) {
                         long serialNo = mUserManager.getSerialNumberForUser(user);
                         allUsers.put(serialNo, user);
                         quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
+
+                        boolean userUnlocked = mUserManager.isUserUnlocked(user);
+
+                        // We can only query for shortcuts when the user is unlocked.
+                        if (userUnlocked) {
+                            List<ShortcutInfoCompat> pinnedShortcuts =
+                                    mDeepShortcutManager.queryForPinnedShortcuts(null, user);
+                            if (mDeepShortcutManager.wasLastCallSuccess()) {
+                                for (ShortcutInfoCompat shortcut : pinnedShortcuts) {
+                                    shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
+                                            shortcut);
+                                }
+                            } else {
+                                // Shortcut manager can fail due to some race condition when the
+                                // lock state changes too frequently. For the purpose of the loading
+                                // shortcuts, consider the user is still locked.
+                                userUnlocked = false;
+                            }
+                        }
+                        unlockedUsers.put(serialNo, userUnlocked);
                     }
 
                     ShortcutInfo info;
@@ -1837,6 +1796,7 @@
                             switch (itemType) {
                             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                                 id = c.getLong(idIndex);
                                 intentDescription = c.getString(intentIndex);
                                 serialNumber = c.getInt(profileIdIndex);
@@ -1889,8 +1849,7 @@
                                             if (intent == null) {
                                                 // The app is installed but the component is no
                                                 // longer available.
-                                                Launcher.addDumpLog(TAG,
-                                                        "Invalid component removed: " + cn, true);
+                                                FileLog.d(TAG, "Invalid component removed: " + cn);
                                                 itemsToRemove.add(id);
                                                 continue;
                                             } else {
@@ -1901,8 +1860,7 @@
                                         } else if (restored) {
                                             // Package is not yet available but might be
                                             // installed later.
-                                            Launcher.addDumpLog(TAG,
-                                                    "package not yet restored: " + cn, true);
+                                            FileLog.d(TAG, "package not yet restored: " + cn);
 
                                             if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) {
                                                 // Restore has started once.
@@ -1927,15 +1885,13 @@
                                                     restored = false;
                                                     itemReplaced = true;
 
-                                                } else if (REMOVE_UNRESTORED_ICONS) {
-                                                    Launcher.addDumpLog(TAG,
-                                                            "Unrestored package removed: " + cn, true);
+                                                } else {
+                                                    FileLog.d(TAG, "Unrestored package removed: " + cn);
                                                     itemsToRemove.add(id);
                                                     continue;
                                                 }
-                                            } else if (REMOVE_UNRESTORED_ICONS) {
-                                                Launcher.addDumpLog(TAG,
-                                                        "Unrestored package removed: " + cn, true);
+                                            } else {
+                                                FileLog.d(TAG, "Unrestored package removed: " + cn);
                                                 itemsToRemove.add(id);
                                                 continue;
                                             }
@@ -1947,8 +1903,7 @@
                                         } else if (!isSdCardReady) {
                                             // SdCard is not ready yet. Package might get available,
                                             // once it is ready.
-                                            Launcher.addDumpLog(TAG, "Invalid package: " + cn
-                                                    + " (check again later)", true);
+                                            Log.d(TAG, "Invalid package: " + cn + " (check again later)");
                                             HashSet<String> pkgs = sPendingPackages.get(user);
                                             if (pkgs == null) {
                                                 pkgs = new HashSet<String>();
@@ -1961,8 +1916,7 @@
                                         } else {
                                             // Do not wait for external media load anymore.
                                             // Log the invalid package, and remove it
-                                            Launcher.addDumpLog(TAG,
-                                                    "Invalid package removed: " + cn, true);
+                                            FileLog.d(TAG, "Invalid package removed: " + cn);
                                             itemsToRemove.add(id);
                                             continue;
                                         }
@@ -1972,8 +1926,7 @@
                                         restored = false;
                                     }
                                 } catch (URISyntaxException e) {
-                                    Launcher.addDumpLog(TAG,
-                                            "Invalid uri: " + intentDescription, true);
+                                    FileLog.d(TAG, "Invalid uri: " + intentDescription);
                                     itemsToRemove.add(id);
                                     continue;
                                 }
@@ -1983,9 +1936,8 @@
 
                                 if (itemReplaced) {
                                     if (user.equals(UserHandleCompat.myUserHandle())) {
-                                        info = getAppShortcutInfo(intent, user, context, null,
-                                                cursorIconInfo.iconIndex, titleIndex,
-                                                false, useLowResIcon);
+                                        info = getAppShortcutInfo(intent, user, null,
+                                                cursorIconInfo, false, useLowResIcon);
                                     } else {
                                         // Don't replace items for other profiles.
                                         itemsToRemove.add(id);
@@ -1993,11 +1945,8 @@
                                     }
                                 } else if (restored) {
                                     if (user.equals(UserHandleCompat.myUserHandle())) {
-                                        Launcher.addDumpLog(TAG,
-                                                "constructing info for partially restored package",
-                                                true);
-                                        info = getRestoredItemInfo(c, titleIndex, intent,
-                                                promiseType, itemType, cursorIconInfo, context);
+                                        info = getRestoredItemInfo(c, intent,
+                                                promiseType, itemType, cursorIconInfo);
                                         intent = getRestoredItemIntent(c, context, intent);
                                     } else {
                                         // Don't restore items for other profiles.
@@ -2006,11 +1955,34 @@
                                     }
                                 } else if (itemType ==
                                         LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-                                    info = getAppShortcutInfo(intent, user, context, c,
-                                            cursorIconInfo.iconIndex, titleIndex,
-                                            allowMissingTarget, useLowResIcon);
-                                } else {
-                                    info = getShortcutInfo(c, context, titleIndex, cursorIconInfo);
+                                    info = getAppShortcutInfo(intent, user, c,
+                                            cursorIconInfo, allowMissingTarget, useLowResIcon);
+                                } else if (itemType ==
+                                        LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+
+                                    ShortcutKey key = ShortcutKey.fromIntent(intent, user);
+                                    if (unlockedUsers.get(serialNumber)) {
+                                        ShortcutInfoCompat pinnedShortcut =
+                                                shortcutKeyToPinnedShortcuts.get(key);
+                                        if (pinnedShortcut == null) {
+                                            // The shortcut is no longer valid.
+                                            itemsToRemove.add(id);
+                                            continue;
+                                        }
+                                        info = new ShortcutInfo(pinnedShortcut, context);
+                                        intent = info.intent;
+                                    } else {
+                                        // Create a shortcut info in disabled mode for now.
+                                        info = new ShortcutInfo();
+                                        info.user = user;
+                                        info.itemType = itemType;
+                                        loadInfoFromCursor(info, c, cursorIconInfo);
+
+                                        info.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
+                                    }
+                                    incrementPinnedShortcutCount(key, false /* shouldPin */);
+                                } else { // item type == ITEM_TYPE_SHORTCUT
+                                    info = getShortcutInfo(c, cursorIconInfo);
 
                                     // Shortcuts are only available on the primary profile
                                     if (PackageManagerHelper.isAppSuspended(manager, targetPackage)) {
@@ -2076,7 +2048,7 @@
                                         // Item is in a user folder
                                         FolderInfo folderInfo =
                                                 findOrMakeFolder(sBgFolders, container);
-                                        folderInfo.add(info);
+                                        folderInfo.add(info, false);
                                         break;
                                     }
                                     sBgItemsIdMap.put(info.id, info);
@@ -2090,7 +2062,7 @@
                                 FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
 
                                 // Do not trim the folder label, as is was set by the user.
-                                folderInfo.title = c.getString(titleIndex);
+                                folderInfo.title = c.getString(cursorIconInfo.titleIndex);
                                 folderInfo.id = id;
                                 folderInfo.container = container;
                                 folderInfo.screenId = c.getInt(screenIndex);
@@ -2159,11 +2131,8 @@
                                 final boolean isProviderReady = isValidProvider(provider);
                                 if (!isSafeMode && !customWidget &&
                                         wasProviderReady && !isProviderReady) {
-                                    String log = "Deleting widget that isn't installed anymore: "
-                                            + "id=" + id + " appWidgetId=" + appWidgetId;
-
-                                    Log.e(TAG, log);
-                                    Launcher.addDumpLog(TAG, log, false);
+                                    FileLog.d(TAG, "Deleting widget that isn't installed anymore: "
+                                            + provider);
                                     itemsToRemove.add(id);
                                 } else {
                                     if (isProviderReady) {
@@ -2181,7 +2150,7 @@
 
                                             // Id would be valid only if the widget restore broadcast was received.
                                             if (isIdValid) {
-                                                status = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+                                                status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
                                             } else {
                                                 status &= ~LauncherAppWidgetInfo
                                                         .FLAG_PROVIDER_NOT_READY;
@@ -2203,9 +2172,8 @@
                                             // App restore has started. Update the flag
                                             appWidgetInfo.restoreStatus |=
                                                     LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
-                                        } else if (REMOVE_UNRESTORED_ICONS && !isSafeMode) {
-                                            Launcher.addDumpLog(TAG,
-                                                    "Unrestored widget removed: " + component, true);
+                                        } else if (!isSafeMode) {
+                                            FileLog.d(TAG, "Unrestored widget removed: " + component);
                                             itemsToRemove.add(id);
                                             continue;
                                         }
@@ -2213,6 +2181,14 @@
                                         appWidgetInfo.installProgress =
                                                 installProgress == null ? 0 : installProgress;
                                     }
+                                    if (appWidgetInfo.hasRestoreFlag(
+                                            LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
+                                        intentDescription = c.getString(intentIndex);
+                                        if (!TextUtils.isEmpty(intentDescription)) {
+                                            appWidgetInfo.bindOptions =
+                                                    Intent.parseUri(intentDescription, 0);
+                                        }
+                                    }
 
                                     appWidgetInfo.id = id;
                                     appWidgetInfo.screenId = c.getInt(screenIndex);
@@ -2257,13 +2233,11 @@
                                 break;
                             }
                         } catch (Exception e) {
-                            Launcher.addDumpLog(TAG, "Desktop items loading interrupted", e, true);
+                            Log.e(TAG, "Desktop items loading interrupted", e);
                         }
                     }
                 } finally {
-                    if (c != null) {
-                        c.close();
-                    }
+                    Utilities.closeSilently(c);
                 }
 
                 // Break early if we've stopped loading
@@ -2283,14 +2257,26 @@
                     }
 
                     // Remove any empty folder
-                    for (long folderId : LauncherAppState.getLauncherProvider()
-                            .deleteEmptyFolders()) {
+                    ArrayList<Long> deletedFolderIds = (ArrayList<Long>) LauncherSettings.Settings
+                            .call(contentResolver,
+                                    LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS)
+                            .getSerializable(LauncherSettings.Settings.EXTRA_VALUE);
+                    for (long folderId : deletedFolderIds) {
                         sBgWorkspaceItems.remove(sBgFolders.get(folderId));
                         sBgFolders.remove(folderId);
                         sBgItemsIdMap.remove(folderId);
                     }
                 }
 
+                // Unpin shortcuts that don't exist on the workspace.
+                for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
+                    MutableInt numTimesPinned = sBgPinnedShortcutCounts.get(key);
+                    if (numTimesPinned == null || numTimesPinned.value == 0) {
+                        // Shortcut is pinned but doesn't exist on the workspace; unpin it.
+                        mDeepShortcutManager.unpinShortcut(key);
+                    }
+                }
+
                 // Sort all the folder items and make sure the first 3 items are high resolution.
                 for (FolderInfo folder : sBgFolders) {
                     Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
@@ -2317,7 +2303,7 @@
 
                 if (!isSdCardReady && !sPendingPackages.isEmpty()) {
                     context.registerReceiver(new AppsAvailabilityCheck(),
-                            new IntentFilter(StartupReceiver.SYSTEM_READY),
+                            new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
                             null, sWorker);
                 }
 
@@ -2349,19 +2335,14 @@
                             if (screenId > 0) {
                                 line += " | ";
                             }
-                            ItemInfo[][] screen = occupied.valueAt(i);
-                            for (int x = 0; x < countX; x++) {
-                                if (x < screen.length && y < screen[x].length) {
-                                    line += (screen[x][y] != null) ? "#" : ".";
-                                } else {
-                                    line += "!";
-                                }
-                            }
                         }
                         Log.d(TAG, "[ " + line + " ]");
                     }
                 }
             }
+            if (LauncherAppState.PROFILE_STARTUP) {
+                Trace.endSection();
+            }
         }
 
         /**
@@ -2439,47 +2420,41 @@
             }
         }
 
-        /** Filters the set of folders which are on the specified screen. */
-        private void filterCurrentFolders(long currentScreenId,
-                LongArrayMap<ItemInfo> itemsIdMap,
-                LongArrayMap<FolderInfo> folders,
-                LongArrayMap<FolderInfo> currentScreenFolders,
-                LongArrayMap<FolderInfo> otherScreenFolders) {
-
-            int total = folders.size();
-            for (int i = 0; i < total; i++) {
-                long id = folders.keyAt(i);
-                FolderInfo folder = folders.valueAt(i);
-
-                ItemInfo info = itemsIdMap.get(id);
-                if (info == null || folder == null) continue;
-                if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
-                        info.screenId == currentScreenId) {
-                    currentScreenFolders.put(id, folder);
-                } else {
-                    otherScreenFolders.put(id, folder);
-                }
-            }
-        }
-
         /** Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to
          * right) */
         private void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
             final LauncherAppState app = LauncherAppState.getInstance();
             final InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
-            // XXX: review this
+            final int screenCols = profile.numColumns;
+            final int screenCellCount = profile.numColumns * profile.numRows;
             Collections.sort(workspaceItems, new Comparator<ItemInfo>() {
                 @Override
                 public int compare(ItemInfo lhs, ItemInfo rhs) {
-                    int cellCountX = (int) profile.numColumns;
-                    int cellCountY = (int) profile.numRows;
-                    int screenOffset = cellCountX * cellCountY;
-                    int containerOffset = screenOffset * (Launcher.SCREEN_COUNT + 1); // +1 hotseat
-                    long lr = (lhs.container * containerOffset + lhs.screenId * screenOffset +
-                            lhs.cellY * cellCountX + lhs.cellX);
-                    long rr = (rhs.container * containerOffset + rhs.screenId * screenOffset +
-                            rhs.cellY * cellCountX + rhs.cellX);
-                    return Utilities.longCompare(lr, rr);
+                    if (lhs.container == rhs.container) {
+                        // Within containers, order by their spatial position in that container
+                        switch ((int) lhs.container) {
+                            case LauncherSettings.Favorites.CONTAINER_DESKTOP: {
+                                long lr = (lhs.screenId * screenCellCount +
+                                        lhs.cellY * screenCols + lhs.cellX);
+                                long rr = (rhs.screenId * screenCellCount +
+                                        rhs.cellY * screenCols + rhs.cellX);
+                                return Utilities.longCompare(lr, rr);
+                            }
+                            case LauncherSettings.Favorites.CONTAINER_HOTSEAT: {
+                                // We currently use the screen id as the rank
+                                return Utilities.longCompare(lhs.screenId, rhs.screenId);
+                            }
+                            default:
+                                if (ProviderConfig.IS_DOGFOOD_BUILD) {
+                                    throw new RuntimeException("Unexpected container type when " +
+                                            "sorting workspace items.");
+                                }
+                                return 0;
+                        }
+                    } else {
+                        // Between containers, order by hotseat, desktop
+                        return Utilities.longCompare(lhs.container, rhs.container);
+                    }
                 }
             });
         }
@@ -2501,10 +2476,7 @@
         private void bindWorkspaceItems(final Callbacks oldCallbacks,
                 final ArrayList<ItemInfo> workspaceItems,
                 final ArrayList<LauncherAppWidgetInfo> appWidgets,
-                final LongArrayMap<FolderInfo> folders,
-                ArrayList<Runnable> deferredBindRunnables) {
-
-            final boolean postOnMainThread = (deferredBindRunnables != null);
+                final Executor executor) {
 
             // Bind the workspace items
             int N = workspaceItems.size();
@@ -2521,32 +2493,7 @@
                         }
                     }
                 };
-                if (postOnMainThread) {
-                    synchronized (deferredBindRunnables) {
-                        deferredBindRunnables.add(r);
-                    }
-                } else {
-                    runOnMainThread(r);
-                }
-            }
-
-            // Bind the folders
-            if (!folders.isEmpty()) {
-                final Runnable r = new Runnable() {
-                    public void run() {
-                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
-                        if (callbacks != null) {
-                            callbacks.bindFolders(folders);
-                        }
-                    }
-                };
-                if (postOnMainThread) {
-                    synchronized (deferredBindRunnables) {
-                        deferredBindRunnables.add(r);
-                    }
-                } else {
-                    runOnMainThread(r);
-                }
+                executor.execute(r);
             }
 
             // Bind the widgets, one at a time
@@ -2561,11 +2508,7 @@
                         }
                     }
                 };
-                if (postOnMainThread) {
-                    deferredBindRunnables.add(r);
-                } else {
-                    runOnMainThread(r);
-                }
+                executor.execute(r);
             }
         }
 
@@ -2586,55 +2529,40 @@
             }
 
             // Save a copy of all the bg-thread collections
-            ArrayList<ItemInfo> workspaceItems = new ArrayList<ItemInfo>();
-            ArrayList<LauncherAppWidgetInfo> appWidgets =
-                    new ArrayList<LauncherAppWidgetInfo>();
-            ArrayList<Long> orderedScreenIds = new ArrayList<Long>();
-
-            final LongArrayMap<FolderInfo> folders;
-            final LongArrayMap<ItemInfo> itemsIdMap;
+            ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
+            ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
+            ArrayList<Long> orderedScreenIds = new ArrayList<>();
 
             synchronized (sBgLock) {
                 workspaceItems.addAll(sBgWorkspaceItems);
                 appWidgets.addAll(sBgAppWidgets);
                 orderedScreenIds.addAll(sBgWorkspaceScreens);
-
-                folders = sBgFolders.clone();
-                itemsIdMap = sBgItemsIdMap.clone();
             }
 
-            final boolean isLoadingSynchronously =
-                    synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE;
-            int currScreen = isLoadingSynchronously ? synchronizeBindPage :
-                oldCallbacks.getCurrentWorkspaceScreen();
-            if (currScreen >= orderedScreenIds.size()) {
-                // There may be no workspace screens (just hotseat items and an empty page).
-                currScreen = PagedView.INVALID_RESTORE_PAGE;
+            final int currentScreen;
+            {
+                int currScreen = synchronizeBindPage != PagedView.INVALID_RESTORE_PAGE
+                        ? synchronizeBindPage : oldCallbacks.getCurrentWorkspaceScreen();
+                if (currScreen >= orderedScreenIds.size()) {
+                    // There may be no workspace screens (just hotseat items and an empty page).
+                    currScreen = PagedView.INVALID_RESTORE_PAGE;
+                }
+                currentScreen = currScreen;
             }
-            final int currentScreen = currScreen;
-            final long currentScreenId = currentScreen < 0
-                    ? INVALID_SCREEN_ID : orderedScreenIds.get(currentScreen);
-
-            // Load all the items that are on the current page first (and in the process, unbind
-            // all the existing workspace items before we call startBinding() below.
-            unbindWorkspaceItemsOnMainThread();
+            final boolean validFirstPage = currentScreen >= 0;
+            final long currentScreenId =
+                    validFirstPage ? orderedScreenIds.get(currentScreen) : INVALID_SCREEN_ID;
 
             // Separate the items that are on the current screen, and all the other remaining items
-            ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<ItemInfo>();
-            ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<ItemInfo>();
-            ArrayList<LauncherAppWidgetInfo> currentAppWidgets =
-                    new ArrayList<LauncherAppWidgetInfo>();
-            ArrayList<LauncherAppWidgetInfo> otherAppWidgets =
-                    new ArrayList<LauncherAppWidgetInfo>();
-            LongArrayMap<FolderInfo> currentFolders = new LongArrayMap<>();
-            LongArrayMap<FolderInfo> otherFolders = new LongArrayMap<>();
+            ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
+            ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
+            ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
+            ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
 
             filterCurrentWorkspaceItems(currentScreenId, workspaceItems, currentWorkspaceItems,
                     otherWorkspaceItems);
             filterCurrentAppWidgets(currentScreenId, appWidgets, currentAppWidgets,
                     otherAppWidgets);
-            filterCurrentFolders(currentScreenId, itemsIdMap, folders, currentFolders,
-                    otherFolders);
             sortWorkspaceItemsSpatially(currentWorkspaceItems);
             sortWorkspaceItemsSpatially(otherWorkspaceItems);
 
@@ -2643,6 +2571,7 @@
                 public void run() {
                     Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                     if (callbacks != null) {
+                        callbacks.clearPendingBinds();
                         callbacks.startBinding();
                     }
                 }
@@ -2651,28 +2580,30 @@
 
             bindWorkspaceScreens(oldCallbacks, orderedScreenIds);
 
-            // Load items on the current page
-            bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets,
-                    currentFolders, null);
-            if (isLoadingSynchronously) {
-                r = new Runnable() {
-                    public void run() {
-                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
-                        if (callbacks != null && currentScreen != PagedView.INVALID_RESTORE_PAGE) {
-                            callbacks.onPageBoundSynchronously(currentScreen);
-                        }
-                    }
-                };
-                runOnMainThread(r);
-            }
+            Executor mainExecutor = new DeferredMainThreadExecutor();
+            // Load items on the current page.
+            bindWorkspaceItems(oldCallbacks, currentWorkspaceItems, currentAppWidgets, mainExecutor);
 
-            // Load all the remaining pages (if we are loading synchronously, we want to defer this
-            // work until after the first render)
-            synchronized (mDeferredBindRunnables) {
-                mDeferredBindRunnables.clear();
-            }
-            bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
-                    (isLoadingSynchronously ? mDeferredBindRunnables : null));
+            // In case of validFirstPage, only bind the first screen, and defer binding the
+            // remaining screens after first onDraw (and an optional the fade animation whichever
+            // happens later).
+            // This ensures that the first screen is immediately visible (eg. during rotation)
+            // In case of !validFirstPage, bind all pages one after other.
+            final Executor deferredExecutor =
+                    validFirstPage ? new ViewOnDrawExecutor(mHandler) : mainExecutor;
+
+            mainExecutor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    Callbacks callbacks = tryGetCallbacks(oldCallbacks);
+                    if (callbacks != null) {
+                        callbacks.finishFirstPageBind(
+                                validFirstPage ? (ViewOnDrawExecutor) deferredExecutor : null);
+                    }
+                }
+            });
+
+            bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, deferredExecutor);
 
             // Tell the workspace that we're done binding items
             r = new Runnable() {
@@ -2702,11 +2633,23 @@
 
                 }
             };
-            if (isLoadingSynchronously) {
-                synchronized (mDeferredBindRunnables) {
-                    mDeferredBindRunnables.add(r);
-                }
-            } else {
+            deferredExecutor.execute(r);
+
+            if (validFirstPage) {
+                r = new Runnable() {
+                    public void run() {
+                        Callbacks callbacks = tryGetCallbacks(oldCallbacks);
+                        if (callbacks != null) {
+                            // We are loading synchronously, which means, some of the pages will be
+                            // bound after first draw. Inform the callbacks that page binding is
+                            // not complete, and schedule the remaining pages.
+                            if (currentScreen != PagedView.INVALID_RESTORE_PAGE) {
+                                callbacks.onPageBoundSynchronously(currentScreen);
+                            }
+                            callbacks.executeOnNextDraw((ViewOnDrawExecutor) deferredExecutor);
+                        }
+                    }
+                };
                 runOnMainThread(r);
             }
         }
@@ -2780,12 +2723,7 @@
                     }
                 }
             };
-            boolean isRunningOnMainThread = !(sWorkerThread.getThreadId() == Process.myTid());
-            if (isRunningOnMainThread) {
-                r.run();
-            } else {
-                mHandler.post(r);
-            }
+            runOnMainThread(r);
         }
 
         private void loadAllApps() {
@@ -2879,6 +2817,32 @@
             }
         }
 
+        private void loadAndBindDeepShortcuts() {
+            if (DEBUG_LOADERS) {
+                Log.d(TAG, "loadAndBindDeepShortcuts mDeepShortcutsLoaded=" + mDeepShortcutsLoaded);
+            }
+            if (!mDeepShortcutsLoaded) {
+                mBgDeepShortcutMap.clear();
+                mHasShortcutHostPermission = mDeepShortcutManager.hasHostPermission();
+                if (mHasShortcutHostPermission) {
+                    for (UserHandleCompat user : mUserManager.getUserProfiles()) {
+                        if (mUserManager.isUserUnlocked(user)) {
+                            List<ShortcutInfoCompat> shortcuts = mDeepShortcutManager
+                                    .queryForAllShortcuts(user);
+                            updateDeepShortcutMap(null, user, shortcuts);
+                        }
+                    }
+                }
+                synchronized (LoaderTask.this) {
+                    if (mStopped) {
+                        return;
+                    }
+                    mDeepShortcutsLoaded = true;
+                }
+            }
+            bindDeepShortcuts();
+        }
+
         public void dumpState() {
             synchronized (sBgLock) {
                 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
@@ -2890,6 +2854,60 @@
     }
 
     /**
+     * Clear all the shortcuts for the given package, and re-add the new shortcuts.
+     */
+    private void updateDeepShortcutMap(
+            String packageName, UserHandleCompat user, List<ShortcutInfoCompat> shortcuts) {
+        if (packageName != null) {
+            Iterator<ComponentKey> keysIter = mBgDeepShortcutMap.keySet().iterator();
+            while (keysIter.hasNext()) {
+                ComponentKey next = keysIter.next();
+                if (next.componentName.getPackageName().equals(packageName)
+                        && next.user.equals(user)) {
+                    keysIter.remove();
+                }
+            }
+        }
+
+        // Now add the new shortcuts to the map.
+        for (ShortcutInfoCompat shortcut : shortcuts) {
+            boolean shouldShowInContainer = shortcut.isEnabled()
+                    && (shortcut.isDeclaredInManifest() || shortcut.isDynamic());
+            if (shouldShowInContainer) {
+                ComponentKey targetComponent
+                        = new ComponentKey(shortcut.getActivity(), shortcut.getUserHandle());
+                mBgDeepShortcutMap.addToList(targetComponent, shortcut.getId());
+            }
+        }
+    }
+
+    public void bindDeepShortcuts() {
+        final MultiHashMap<ComponentKey, String> shortcutMapCopy = mBgDeepShortcutMap.clone();
+        Runnable r = new Runnable() {
+            @Override
+            public void run() {
+                Callbacks callbacks = getCallback();
+                if (callbacks != null) {
+                    callbacks.bindDeepShortcutMap(shortcutMapCopy);
+                }
+            }
+        };
+        runOnMainThread(r);
+    }
+
+    /**
+     * Refreshes the cached shortcuts if the shortcut permission has changed.
+     * Current implementation simply reloads the workspace, but it can be optimized to
+     * use partial updates similar to {@link UserManagerCompat}
+     */
+    public void refreshShortcutsIfRequired() {
+        if (Utilities.isNycMR1OrAbove()) {
+            sWorker.removeCallbacks(mShortcutPermissionCheckRunnable);
+            sWorker.post(mShortcutPermissionCheckRunnable);
+        }
+    }
+
+    /**
      * Called when the icons for packages have been updated in the icon cache.
      */
     public void onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandleCompat user) {
@@ -2914,19 +2932,7 @@
             mBgAllAppsList.updateIconsAndLabels(updatedPackages, user, updatedApps);
         }
 
-        if (!updatedShortcuts.isEmpty()) {
-            final UserHandleCompat userFinal = user;
-            mHandler.post(new Runnable() {
-
-                public void run() {
-                    Callbacks cb = getCallback();
-                    if (cb != null && callbacks == cb) {
-                        cb.bindShortcutsChanged(updatedShortcuts,
-                                new ArrayList<ShortcutInfo>(), userFinal);
-                    }
-                }
-            });
-        }
+        bindUpdatedShortcuts(updatedShortcuts, user);
 
         if (!updatedApps.isEmpty()) {
             mHandler.post(new Runnable() {
@@ -2941,7 +2947,30 @@
         }
     }
 
-    void enqueuePackageUpdated(PackageUpdatedTask task) {
+    private void bindUpdatedShortcuts(
+            ArrayList<ShortcutInfo> updatedShortcuts, UserHandleCompat user) {
+        bindUpdatedShortcuts(updatedShortcuts, new ArrayList<ShortcutInfo>(), user);
+    }
+
+    private void bindUpdatedShortcuts(
+            final ArrayList<ShortcutInfo> updatedShortcuts,
+            final ArrayList<ShortcutInfo> removedShortcuts,
+            final UserHandleCompat user) {
+        if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) {
+            final Callbacks callbacks = getCallback();
+            mHandler.post(new Runnable() {
+
+                public void run() {
+                    Callbacks cb = getCallback();
+                    if (cb != null && callbacks == cb) {
+                        cb.bindShortcutsChanged(updatedShortcuts, removedShortcuts, user);
+                    }
+                }
+            });
+        }
+    }
+
+    void enqueueItemUpdatedTask(Runnable task) {
         sWorker.post(task);
     }
 
@@ -2964,17 +2993,16 @@
                             if (PackageManagerHelper.isAppOnSdcard(manager, pkg)) {
                                 packagesUnavailable.add(pkg);
                             } else {
-                                Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
                                 packagesRemoved.add(pkg);
                             }
                         }
                     }
                     if (!packagesRemoved.isEmpty()) {
-                        enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
+                        enqueueItemUpdatedTask(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
                                 packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
                     }
                     if (!packagesUnavailable.isEmpty()) {
-                        enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UNAVAILABLE,
+                        enqueueItemUpdatedTask(new PackageUpdatedTask(PackageUpdatedTask.OP_UNAVAILABLE,
                                 packagesUnavailable.toArray(new String[packagesUnavailable.size()]), user));
                     }
                 }
@@ -3227,22 +3255,11 @@
                     }
                 }
 
-                if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) {
-                    final Callbacks callbacks = getCallback();
-                    mHandler.post(new Runnable() {
-
-                        public void run() {
-                            Callbacks cb = getCallback();
-                            if (callbacks == cb && cb != null) {
-                                callbacks.bindShortcutsChanged(
-                                        updatedShortcuts, removedShortcuts, mUser);
-                            }
-                        }
-                    });
-                    if (!removedShortcuts.isEmpty()) {
-                        deleteItemsFromDatabase(context, removedShortcuts);
-                    }
+                bindUpdatedShortcuts(updatedShortcuts, removedShortcuts, mUser);
+                if (!removedShortcuts.isEmpty()) {
+                    deleteItemsFromDatabase(context, removedShortcuts);
                 }
+
                 if (!widgets.isEmpty()) {
                     final Callbacks callbacks = getCallback();
                     mHandler.post(new Runnable() {
@@ -3332,6 +3349,174 @@
         }
     }
 
+    /**
+     * Repopulates the shortcut info, possibly updating any icon already on the workspace.
+     */
+    public void updateShortcutInfo(final ShortcutInfoCompat fullDetail, final ShortcutInfo info) {
+        enqueueItemUpdatedTask(new Runnable() {
+            @Override
+            public void run() {
+                info.updateFromDeepShortcutInfo(
+                        fullDetail, LauncherAppState.getInstance().getContext());
+                ArrayList<ShortcutInfo> update = new ArrayList<ShortcutInfo>();
+                update.add(info);
+                bindUpdatedShortcuts(update, fullDetail.getUserHandle());
+            }
+        });
+    }
+
+    private class ShortcutsChangedTask implements Runnable {
+        private final String mPackageName;
+        private final List<ShortcutInfoCompat> mShortcuts;
+        private final UserHandleCompat mUser;
+        private final boolean mUpdateIdMap;
+
+        public ShortcutsChangedTask(String packageName, List<ShortcutInfoCompat> shortcuts,
+                UserHandleCompat user, boolean updateIdMap) {
+            mPackageName = packageName;
+            mShortcuts = shortcuts;
+            mUser = user;
+            mUpdateIdMap = updateIdMap;
+        }
+
+        @Override
+        public void run() {
+            mDeepShortcutManager.onShortcutsChanged(mShortcuts);
+
+            // Find ShortcutInfo's that have changed on the workspace.
+            final ArrayList<ShortcutInfo> removedShortcutInfos = new ArrayList<>();
+            MultiHashMap<String, ShortcutInfo> idsToWorkspaceShortcutInfos = new MultiHashMap<>();
+            for (ItemInfo itemInfo : sBgItemsIdMap) {
+                if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                    ShortcutInfo si = (ShortcutInfo) itemInfo;
+                    if (si.getPromisedIntent().getPackage().equals(mPackageName)
+                            && si.user.equals(mUser)) {
+                        idsToWorkspaceShortcutInfos.addToList(si.getDeepShortcutId(), si);
+                    }
+                }
+            }
+
+            final Context context = LauncherAppState.getInstance().getContext();
+            final ArrayList<ShortcutInfo> updatedShortcutInfos = new ArrayList<>();
+            if (!idsToWorkspaceShortcutInfos.isEmpty()) {
+                // Update the workspace to reflect the changes to updated shortcuts residing on it.
+                List<ShortcutInfoCompat> shortcuts = mDeepShortcutManager.queryForFullDetails(
+                        mPackageName, new ArrayList<>(idsToWorkspaceShortcutInfos.keySet()), mUser);
+                for (ShortcutInfoCompat fullDetails : shortcuts) {
+                    List<ShortcutInfo> shortcutInfos = idsToWorkspaceShortcutInfos
+                            .remove(fullDetails.getId());
+                    if (!fullDetails.isPinned()) {
+                        // The shortcut was previously pinned but is no longer, so remove it from
+                        // the workspace and our pinned shortcut counts.
+                        // Note that we put this check here, after querying for full details,
+                        // because there's a possible race condition between pinning and
+                        // receiving this callback.
+                        removedShortcutInfos.addAll(shortcutInfos);
+                        continue;
+                    }
+                    for (ShortcutInfo shortcutInfo : shortcutInfos) {
+                        shortcutInfo.updateFromDeepShortcutInfo(fullDetails, context);
+                        updatedShortcutInfos.add(shortcutInfo);
+                    }
+                }
+            }
+
+            // If there are still entries in idsToWorkspaceShortcutInfos, that means that
+            // the corresponding shortcuts weren't passed in onShortcutsChanged(). This
+            // means they were cleared, so we remove and unpin them now.
+            for (String id : idsToWorkspaceShortcutInfos.keySet()) {
+                removedShortcutInfos.addAll(idsToWorkspaceShortcutInfos.get(id));
+            }
+
+            bindUpdatedShortcuts(updatedShortcutInfos, removedShortcutInfos, mUser);
+            if (!removedShortcutInfos.isEmpty()) {
+                deleteItemsFromDatabase(context, removedShortcutInfos);
+            }
+
+            if (mUpdateIdMap) {
+                // Update the deep shortcut map if the list of ids has changed for an activity.
+                updateDeepShortcutMap(mPackageName, mUser, mShortcuts);
+                bindDeepShortcuts();
+            }
+        }
+    }
+
+    /**
+     * Task to handle changing of lock state of the user
+     */
+    private class UserLockStateChangedTask implements Runnable {
+
+        private final UserHandleCompat mUser;
+
+        public UserLockStateChangedTask(UserHandleCompat user) {
+            mUser = user;
+        }
+
+        @Override
+        public void run() {
+            boolean isUserUnlocked = mUserManager.isUserUnlocked(mUser);
+            Context context = mApp.getContext();
+
+            HashMap<ShortcutKey, ShortcutInfoCompat> pinnedShortcuts = new HashMap<>();
+            if (isUserUnlocked) {
+                List<ShortcutInfoCompat> shortcuts =
+                        mDeepShortcutManager.queryForPinnedShortcuts(null, mUser);
+                if (mDeepShortcutManager.wasLastCallSuccess()) {
+                    for (ShortcutInfoCompat shortcut : shortcuts) {
+                        pinnedShortcuts.put(ShortcutKey.fromInfo(shortcut), shortcut);
+                    }
+                } else {
+                    // Shortcut manager can fail due to some race condition when the lock state
+                    // changes too frequently. For the purpose of the update,
+                    // consider it as still locked.
+                    isUserUnlocked = false;
+                }
+            }
+
+            // Update the workspace to reflect the changes to updated shortcuts residing on it.
+            ArrayList<ShortcutInfo> updatedShortcutInfos = new ArrayList<>();
+            ArrayList<ShortcutInfo> deletedShortcutInfos = new ArrayList<>();
+            for (ItemInfo itemInfo : sBgItemsIdMap) {
+                if (itemInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+                        && mUser.equals(itemInfo.user)) {
+                    ShortcutInfo si = (ShortcutInfo) itemInfo;
+                    if (isUserUnlocked) {
+                        ShortcutInfoCompat shortcut =
+                                pinnedShortcuts.get(ShortcutKey.fromShortcutInfo(si));
+                        // We couldn't verify the shortcut during loader. If its no longer available
+                        // (probably due to clear data), delete the workspace item as well
+                        if (shortcut == null) {
+                            deletedShortcutInfos.add(si);
+                            continue;
+                        }
+                        si.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
+                        si.updateFromDeepShortcutInfo(shortcut, context);
+                    } else {
+                        si.isDisabled |= ShortcutInfo.FLAG_DISABLED_LOCKED_USER;
+                    }
+                    updatedShortcutInfos.add(si);
+                }
+            }
+            bindUpdatedShortcuts(updatedShortcutInfos, deletedShortcutInfos, mUser);
+            if (!deletedShortcutInfos.isEmpty()) {
+                deleteItemsFromDatabase(context, deletedShortcutInfos);
+            }
+
+            // Remove shortcut id map for that user
+            Iterator<ComponentKey> keysIter = mBgDeepShortcutMap.keySet().iterator();
+            while (keysIter.hasNext()) {
+                if (keysIter.next().user.equals(mUser)) {
+                    keysIter.remove();
+                }
+            }
+
+            if (isUserUnlocked) {
+                updateDeepShortcutMap(null, mUser, mDeepShortcutManager.queryForAllShortcuts(mUser));
+            }
+            bindDeepShortcuts();
+        }
+    }
+
     private void bindWidgetsModel(final Callbacks callbacks, final WidgetsModel model) {
         mHandler.post(new Runnable() {
             @Override
@@ -3392,12 +3577,12 @@
      * Make an ShortcutInfo object for a restored application or shortcut item that points
      * to a package that is not yet installed on the system.
      */
-    public ShortcutInfo getRestoredItemInfo(Cursor c, int titleIndex, Intent intent,
-            int promiseType, int itemType, CursorIconInfo iconInfo, Context context) {
+    public ShortcutInfo getRestoredItemInfo(Cursor c, Intent intent,
+            int promiseType, int itemType, CursorIconInfo iconInfo) {
         final ShortcutInfo info = new ShortcutInfo();
         info.user = UserHandleCompat.myUserHandle();
 
-        Bitmap icon = iconInfo.loadIcon(c, info, context);
+        Bitmap icon = iconInfo.loadIcon(c, info);
         // the fallback icon
         if (icon == null) {
             mIconCache.getTitleAndIcon(info, intent, info.user, false /* useLowResIcon */);
@@ -3406,13 +3591,13 @@
         }
 
         if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
-            String title = (c != null) ? c.getString(titleIndex) : null;
+            String title = iconInfo.getTitle(c);
             if (!TextUtils.isEmpty(title)) {
                 info.title = Utilities.trim(title);
             }
         } else if  ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
             if (TextUtils.isEmpty(info.title)) {
-                info.title = (c != null) ? Utilities.trim(c.getString(titleIndex)) : "";
+                info.title = iconInfo.getTitle(c);
             }
         } else {
             throw new InvalidParameterException("Invalid restoreType " + promiseType);
@@ -3449,7 +3634,7 @@
      * If c is not null, then it will be used to fill in missing data like the title and icon.
      */
     public ShortcutInfo getAppShortcutInfo(Intent intent,
-            UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
+            UserHandleCompat user, Cursor c, CursorIconInfo iconInfo,
             boolean allowMissingTarget, boolean useLowResIcon) {
         if (user == null) {
             Log.d(TAG, "Null user found in getShortcutInfo");
@@ -3474,7 +3659,7 @@
         final ShortcutInfo info = new ShortcutInfo();
         mIconCache.getTitleAndIcon(info, componentName, lai, user, false, useLowResIcon);
         if (mIconCache.isDefaultIcon(info.getIcon(mIconCache), user) && c != null) {
-            Bitmap icon = Utilities.createIconBitmap(c, iconIndex, context);
+            Bitmap icon = iconInfo.loadIcon(c);
             info.setIcon(icon == null ? mIconCache.getDefaultIcon(user) : icon);
         }
 
@@ -3484,7 +3669,7 @@
 
         // from the db
         if (TextUtils.isEmpty(info.title) && c != null) {
-            info.title =  Utilities.trim(c.getString(titleIndex));
+            info.title = iconInfo.getTitle(c);
         }
 
         // fall back to the class name of the activity
@@ -3548,8 +3733,7 @@
     /**
      * Make an ShortcutInfo object for a shortcut that isn't an application.
      */
-    @Thunk ShortcutInfo getShortcutInfo(Cursor c, Context context,
-            int titleIndex, CursorIconInfo iconInfo) {
+    @Thunk ShortcutInfo getShortcutInfo(Cursor c, CursorIconInfo iconInfo) {
         final ShortcutInfo info = new ShortcutInfo();
         // Non-app shortcuts are only supported for current user.
         info.user = UserHandleCompat.myUserHandle();
@@ -3557,16 +3741,22 @@
 
         // TODO: If there's an explicit component and we can't install that, delete it.
 
-        info.title = Utilities.trim(c.getString(titleIndex));
+        loadInfoFromCursor(info, c, iconInfo);
+        return info;
+    }
 
-        Bitmap icon = iconInfo.loadIcon(c, info, context);
+    /**
+     * Make an ShortcutInfo object for a shortcut that isn't an application.
+     */
+    public void loadInfoFromCursor(ShortcutInfo info, Cursor c, CursorIconInfo iconInfo) {
+        info.title = iconInfo.getTitle(c);
+        Bitmap icon = iconInfo.loadIcon(c, info);
         // the fallback icon
         if (icon == null) {
             icon = mIconCache.getDefaultIcon(info.user);
             info.usingFallbackIcon = true;
         }
         info.setIcon(icon);
-        return info;
     }
 
     ShortcutInfo infoFromShortcutIntent(Context context, Intent data) {
@@ -3610,7 +3800,6 @@
         info.title = Utilities.trim(name);
         info.contentDescription = mUserManager.getBadgedLabelForUser(info.title, info.user);
         info.intent = intent;
-        info.customIcon = customIcon;
         info.iconResource = iconResource;
 
         return info;
@@ -3663,6 +3852,14 @@
         }
     }
 
+    @Thunk class DeferredMainThreadExecutor implements Executor {
+
+        @Override
+        public void execute(Runnable command) {
+            runOnMainThread(command);
+        }
+    }
+
     /**
      * @return the looper for the worker thread which can be used to start background tasks.
      */
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 7c4b78d..f3d9493 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -23,7 +23,6 @@
 import android.content.ContentProvider;
 import android.content.ContentProviderOperation;
 import android.content.ContentProviderResult;
-import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
@@ -42,65 +41,68 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
 import android.os.Process;
-import android.os.StrictMode;
+import android.os.Trace;
 import android.os.UserManager;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.SparseArray;
 
 import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
 import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherSettings.WorkspaceScreens;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.dynamicui.ExtractionUtils;
+import com.android.launcher3.provider.LauncherDbUtils;
+import com.android.launcher3.provider.RestoreDbTask;
 import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.util.NoLocaleSqliteContext;
+import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.Thunk;
 
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
+import java.util.Locale;
 
 public class LauncherProvider extends ContentProvider {
     private static final String TAG = "LauncherProvider";
     private static final boolean LOGD = false;
 
-    private static final int DATABASE_VERSION = 26;
+    private static final int DATABASE_VERSION = 27;
 
     public static final String AUTHORITY = ProviderConfig.AUTHORITY;
 
-    static final String TABLE_FAVORITES = LauncherSettings.Favorites.TABLE_NAME;
-    static final String TABLE_WORKSPACE_SCREENS = LauncherSettings.WorkspaceScreens.TABLE_NAME;
     static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
 
     private static final String RESTRICTION_PACKAGE_NAME = "workspace.configuration.package.name";
 
-    @Thunk LauncherProviderChangeListener mListener;
+    private final ChangeListenerWrapper mListenerWrapper = new ChangeListenerWrapper();
+    private Handler mListenerHandler;
+
     protected DatabaseHelper mOpenHelper;
 
     @Override
     public boolean onCreate() {
-        final Context context = getContext();
-        // The content provider exists for the entire duration of the launcher main process and
-        // is the first component to get created. Initializing application context here ensures
-        // that LauncherAppState always exists in the main process.
-        LauncherAppState.setApplicationContext(context.getApplicationContext());
-        StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
-        mOpenHelper = new DatabaseHelper(context);
-        StrictMode.setThreadPolicy(oldPolicy);
+        if (ProviderConfig.IS_DOGFOOD_BUILD) {
+            Log.d(TAG, "Launcher process started");
+        }
+        mListenerHandler = new Handler(mListenerWrapper);
+
         LauncherAppState.setLauncherProvider(this);
         return true;
     }
 
-    public boolean wasNewDbCreated() {
-        return mOpenHelper.wasNewDbCreated();
-    }
-
+    /**
+     * Sets a provider listener.
+     */
     public void setLauncherProviderChangeListener(LauncherProviderChangeListener listener) {
-        mListener = listener;
-        mOpenHelper.mListener = mListener;
+        Preconditions.assertUIThread();
+        mListenerWrapper.mListener = listener;
     }
 
     @Override
@@ -118,13 +120,30 @@
      */
     protected synchronized void createDbIfNotExists() {
         if (mOpenHelper == null) {
-            mOpenHelper = new DatabaseHelper(getContext());
+            if (LauncherAppState.PROFILE_STARTUP) {
+                Trace.beginSection("Opening workspace DB");
+            }
+            mOpenHelper = new DatabaseHelper(getContext(), mListenerHandler);
+
+            if (RestoreDbTask.isPending(getContext())) {
+                if (!RestoreDbTask.performRestore(mOpenHelper)) {
+                    mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
+                }
+                // Set is pending to false irrespective of the result, so that it doesn't get
+                // executed again.
+                RestoreDbTask.setPending(getContext(), false);
+            }
+
+            if (LauncherAppState.PROFILE_STARTUP) {
+                Trace.endSection();
+            }
         }
     }
 
     @Override
     public Cursor query(Uri uri, String[] projection, String selection,
             String[] selectionArgs, String sortOrder) {
+        createDbIfNotExists();
 
         SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
@@ -160,11 +179,12 @@
 
     @Override
     public Uri insert(Uri uri, ContentValues initialValues) {
+        createDbIfNotExists();
         SqlArguments args = new SqlArguments(uri);
 
         // In very limited cases, we support system|signature permission apps to modify the db.
         if (Binder.getCallingPid() != Process.myPid()) {
-            if (!mOpenHelper.initializeExternalAdd(initialValues)) {
+            if (!initializeExternalAdd(initialValues)) {
                 return null;
             }
         }
@@ -194,9 +214,62 @@
         return uri;
     }
 
+    private boolean initializeExternalAdd(ContentValues values) {
+        // 1. Ensure that externally added items have a valid item id
+        long id = mOpenHelper.generateNewItemId();
+        values.put(LauncherSettings.Favorites._ID, id);
+
+        // 2. In the case of an app widget, and if no app widget id is specified, we
+        // attempt allocate and bind the widget.
+        Integer itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE);
+        if (itemType != null &&
+                itemType.intValue() == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET &&
+                !values.containsKey(LauncherSettings.Favorites.APPWIDGET_ID)) {
+
+            final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getContext());
+            ComponentName cn = ComponentName.unflattenFromString(
+                    values.getAsString(Favorites.APPWIDGET_PROVIDER));
+
+            if (cn != null) {
+                try {
+                    int appWidgetId = new AppWidgetHost(getContext(), Launcher.APPWIDGET_HOST_ID)
+                            .allocateAppWidgetId();
+                    values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
+                    if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) {
+                        return false;
+                    }
+                } catch (RuntimeException e) {
+                    Log.e(TAG, "Failed to initialize external widget", e);
+                    return false;
+                }
+            } else {
+                return false;
+            }
+        }
+
+        // Add screen id if not present
+        long screenId = values.getAsLong(LauncherSettings.Favorites.SCREEN);
+        SQLiteStatement stmp = null;
+        try {
+            stmp = mOpenHelper.getWritableDatabase().compileStatement(
+                    "INSERT OR IGNORE INTO workspaceScreens (_id, screenRank) " +
+                            "select ?, (ifnull(MAX(screenRank), -1)+1) from workspaceScreens");
+            stmp.bindLong(1, screenId);
+
+            ContentValues valuesInserted = new ContentValues();
+            valuesInserted.put(LauncherSettings.BaseLauncherColumns._ID, stmp.executeInsert());
+            mOpenHelper.checkId(WorkspaceScreens.TABLE_NAME, valuesInserted);
+            return true;
+        } catch (Exception e) {
+            return false;
+        } finally {
+            Utilities.closeSilently(stmp);
+        }
+    }
 
     @Override
     public int bulkInsert(Uri uri, ContentValues[] values) {
+        createDbIfNotExists();
         SqlArguments args = new SqlArguments(uri);
 
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
@@ -222,6 +295,7 @@
     @Override
     public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws OperationApplicationException {
+        createDbIfNotExists();
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         db.beginTransaction();
         try {
@@ -236,18 +310,42 @@
 
     @Override
     public int delete(Uri uri, String selection, String[] selectionArgs) {
+        createDbIfNotExists();
         SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
 
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
-        int count = db.delete(args.table, args.where, args.args);
-        if (count > 0) notifyListeners();
 
-        reloadLauncherIfExternal();
+        if (Binder.getCallingPid() != Process.myPid()
+                && Favorites.TABLE_NAME.equalsIgnoreCase(args.table)) {
+            String widgetSelection = TextUtils.isEmpty(args.where) ? "1=1" : args.where;
+            widgetSelection = String.format(Locale.ENGLISH, "%1$s = %2$d AND ( %3$s )",
+                    Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET, widgetSelection);
+            try (Cursor c = db.query(Favorites.TABLE_NAME, new String[] { Favorites.APPWIDGET_ID },
+                    widgetSelection, args.args, null, null, null)) {
+                AppWidgetHost host = new AppWidgetHost(getContext(), Launcher.APPWIDGET_HOST_ID);
+                while (c.moveToNext()) {
+                    int widgetId = c.getInt(0);
+                    if (widgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
+                        try {
+                            host.deleteAppWidgetId(widgetId);
+                        } catch (RuntimeException e) {
+                            Log.e(TAG, "Error deleting widget id " + widgetId, e);
+                        }
+                    }
+                }
+            }
+        }
+        int count = db.delete(args.table, args.where, args.args);
+        if (count > 0) {
+            notifyListeners();
+            reloadLauncherIfExternal();
+        }
         return count;
     }
 
     @Override
     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        createDbIfNotExists();
         SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
 
         addModifiedTime(values);
@@ -260,37 +358,64 @@
     }
 
     @Override
-    public Bundle call(String method, String arg, Bundle extras) {
+    public Bundle call(String method, final String arg, final Bundle extras) {
         if (Binder.getCallingUid() != Process.myUid()) {
             return null;
         }
+        createDbIfNotExists();
 
         switch (method) {
-            case LauncherSettings.Settings.METHOD_GET_BOOLEAN: {
+            case LauncherSettings.Settings.METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID: {
+                String extractedColors = extras.getString(
+                        LauncherSettings.Settings.EXTRA_EXTRACTED_COLORS);
+                int wallpaperId = extras.getInt(LauncherSettings.Settings.EXTRA_WALLPAPER_ID);
+                Utilities.getPrefs(getContext()).edit()
+                        .putString(ExtractionUtils.EXTRACTED_COLORS_PREFERENCE_KEY, extractedColors)
+                        .putInt(ExtractionUtils.WALLPAPER_ID_PREFERENCE_KEY, wallpaperId)
+                        .apply();
+                mListenerHandler.sendEmptyMessage(ChangeListenerWrapper.MSG_EXTRACTED_COLORS_CHANGED);
                 Bundle result = new Bundle();
-                if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(arg)) {
-                    result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
-                            Utilities.isAllowRotationPrefEnabled(getContext()));
-                } else {
-                    result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
-                            Utilities.getPrefs(getContext()).getBoolean(arg, extras.getBoolean(
-                                    LauncherSettings.Settings.EXTRA_DEFAULT_VALUE)));
-                }
+                result.putString(LauncherSettings.Settings.EXTRA_VALUE, extractedColors);
                 return result;
             }
-            case LauncherSettings.Settings.METHOD_SET_BOOLEAN: {
-                boolean value = extras.getBoolean(LauncherSettings.Settings.EXTRA_VALUE);
-                Utilities.getPrefs(getContext()).edit().putBoolean(arg, value).apply();
-                if (mListener != null) {
-                    mListener.onSettingsChanged(arg, value);
-                }
-                if (extras.getBoolean(LauncherSettings.Settings.NOTIFY_BACKUP)) {
-                    LauncherBackupAgentHelper.dataChanged(getContext());
-                }
+            case LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG: {
+                clearFlagEmptyDbCreated();
+                return null;
+            }
+            case LauncherSettings.Settings.METHOD_WAS_EMPTY_DB_CREATED : {
                 Bundle result = new Bundle();
-                result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, value);
+                result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
+                        Utilities.getPrefs(getContext()).getBoolean(EMPTY_DATABASE_CREATED, false));
                 return result;
             }
+            case LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS: {
+                Bundle result = new Bundle();
+                result.putSerializable(LauncherSettings.Settings.EXTRA_VALUE, deleteEmptyFolders());
+                return result;
+            }
+            case LauncherSettings.Settings.METHOD_NEW_ITEM_ID: {
+                Bundle result = new Bundle();
+                result.putLong(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewItemId());
+                return result;
+            }
+            case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: {
+                Bundle result = new Bundle();
+                result.putLong(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewScreenId());
+                return result;
+            }
+            case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: {
+                createEmptyDB();
+                return null;
+            }
+            case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
+                loadDefaultFavoritesIfNecessary();
+                return null;
+            }
+            case LauncherSettings.Settings.METHOD_DELETE_DB: {
+                // Are you sure? (y/n)
+                mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
+                return null;
+            }
         }
         return null;
     }
@@ -299,8 +424,8 @@
      * Deletes any empty folder from the DB.
      * @return Ids of deleted folders.
      */
-    public List<Long> deleteEmptyFolders() {
-        ArrayList<Long> folderIds = new ArrayList<Long>();
+    private ArrayList<Long> deleteEmptyFolders() {
+        ArrayList<Long> folderIds = new ArrayList<>();
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         db.beginTransaction();
         try {
@@ -309,16 +434,16 @@
                     + LauncherSettings.Favorites.ITEM_TYPE_FOLDER + " AND "
                     + LauncherSettings.Favorites._ID +  " NOT IN (SELECT " +
                             LauncherSettings.Favorites.CONTAINER + " FROM "
-                                + TABLE_FAVORITES + ")";
-            Cursor c = db.query(TABLE_FAVORITES,
+                                + Favorites.TABLE_NAME + ")";
+            Cursor c = db.query(Favorites.TABLE_NAME,
                     new String[] {LauncherSettings.Favorites._ID},
                     selection, null, null, null, null);
             while (c.moveToNext()) {
                 folderIds.add(c.getLong(0));
             }
             c.close();
-            if (folderIds.size() > 0) {
-                db.delete(TABLE_FAVORITES, Utilities.createDbSelectionQuery(
+            if (!folderIds.isEmpty()) {
+                db.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
                         LauncherSettings.Favorites._ID, folderIds), null);
             }
             db.setTransactionSuccessful();
@@ -335,33 +460,21 @@
      * Overridden in tests
      */
     protected void notifyListeners() {
-        // always notify the backup agent
-        LauncherBackupAgentHelper.dataChanged(getContext());
-        if (mListener != null) {
-            mListener.onLauncherProviderChange();
-        }
+        mListenerHandler.sendEmptyMessage(ChangeListenerWrapper.MSG_LAUNCHER_PROVIDER_CHANGED);
     }
 
     @Thunk static void addModifiedTime(ContentValues values) {
         values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis());
     }
 
-    public long generateNewItemId() {
-        return mOpenHelper.generateNewItemId();
-    }
-
-    public long generateNewScreenId() {
-        return mOpenHelper.generateNewScreenId();
-    }
-
     /**
      * Clears all the data for a fresh start.
      */
-    synchronized public void createEmptyDB() {
+    synchronized private void createEmptyDB() {
         mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
     }
 
-    public void clearFlagEmptyDbCreated() {
+    private void clearFlagEmptyDbCreated() {
         Utilities.getPrefs(getContext()).edit().remove(EMPTY_DATABASE_CREATED).commit();
     }
 
@@ -372,16 +485,16 @@
      *   3) From a partner configuration APK, already in the system image
      *   4) The default configuration for the particular device
      */
-    synchronized public void loadDefaultFavoritesIfNecessary() {
+    synchronized private void loadDefaultFavoritesIfNecessary() {
         SharedPreferences sp = Utilities.getPrefs(getContext());
 
         if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
             Log.d(TAG, "loading default workspace");
 
-            AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction();
+            AppWidgetHost widgetHost = new AppWidgetHost(getContext(), Launcher.APPWIDGET_HOST_ID);
+            AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(widgetHost);
             if (loader == null) {
-                loader = AutoInstallsLayout.get(getContext(),
-                        mOpenHelper.mAppWidgetHost, mOpenHelper);
+                loader = AutoInstallsLayout.get(getContext(),widgetHost, mOpenHelper);
             }
             if (loader == null) {
                 final Partner partner = Partner.get(getContext().getPackageManager());
@@ -390,7 +503,7 @@
                     int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
                             "xml", partner.getPackageName());
                     if (workspaceResId != 0) {
-                        loader = new DefaultLayoutParser(getContext(), mOpenHelper.mAppWidgetHost,
+                        loader = new DefaultLayoutParser(getContext(), widgetHost,
                                 mOpenHelper, partnerRes, workspaceResId);
                     }
                 }
@@ -398,7 +511,7 @@
 
             final boolean usingExternallyProvidedLayout = loader != null;
             if (loader == null) {
-                loader = getDefaultLayoutParser();
+                loader = getDefaultLayoutParser(widgetHost);
             }
 
             // There might be some partially restored DB items, due to buggy restore logic in
@@ -410,7 +523,7 @@
                 // Unable to load external layout. Cleanup and load the internal layout.
                 createEmptyDB();
                 mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(),
-                        getDefaultLayoutParser());
+                        getDefaultLayoutParser(widgetHost));
             }
             clearFlagEmptyDbCreated();
         }
@@ -422,7 +535,7 @@
      * @return the loader if the restrictions are set and the resource exists; null otherwise.
      */
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
-    private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction() {
+    private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(AppWidgetHost widgetHost) {
         // UserManager.getApplicationRestrictions() requires minSdkVersion >= 18
         if (!Utilities.ATLEAST_JB_MR2) {
             return null;
@@ -441,7 +554,7 @@
                 Resources targetResources = ctx.getPackageManager()
                         .getResourcesForApplication(packageName);
                 return AutoInstallsLayout.get(ctx, packageName, targetResources,
-                        mOpenHelper.mAppWidgetHost, mOpenHelper);
+                        widgetHost, mOpenHelper);
             } catch (NameNotFoundException e) {
                 Log.e(TAG, "Target package for restricted profile not found", e);
                 return null;
@@ -450,60 +563,48 @@
         return null;
     }
 
-    private DefaultLayoutParser getDefaultLayoutParser() {
+    private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
         int defaultLayout = LauncherAppState.getInstance()
                 .getInvariantDeviceProfile().defaultLayoutId;
-        return new DefaultLayoutParser(getContext(), mOpenHelper.mAppWidgetHost,
+        return new DefaultLayoutParser(getContext(), widgetHost,
                 mOpenHelper, getContext().getResources(), defaultLayout);
     }
 
-    public void migrateLauncher2Shortcuts() {
-        mOpenHelper.migrateLauncher2Shortcuts(mOpenHelper.getWritableDatabase(),
-                Uri.parse(getContext().getString(R.string.old_launcher_provider_uri)));
-    }
-
-    public void updateFolderItemsRank() {
-        mOpenHelper.updateFolderItemsRank(mOpenHelper.getWritableDatabase(), false);
-    }
-
-    public void convertShortcutsToLauncherActivities() {
-        mOpenHelper.convertShortcutsToLauncherActivities(mOpenHelper.getWritableDatabase());
-    }
-
-
-    public void deleteDatabase() {
-        // Are you sure? (y/n)
-        mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
-    }
-
     /**
      * The class is subclassed in tests to create an in-memory db.
      */
-    protected static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback {
+    public static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback {
+        private final Handler mWidgetHostResetHandler;
         private final Context mContext;
-        @Thunk final AppWidgetHost mAppWidgetHost;
         private long mMaxItemId = -1;
         private long mMaxScreenId = -1;
 
-        private boolean mNewDbCreated = false;
-
-        @Thunk LauncherProviderChangeListener mListener;
-
-        DatabaseHelper(Context context) {
-            super(context, LauncherFiles.LAUNCHER_DB, null, DATABASE_VERSION);
-            mContext = context;
-            mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
-
+        DatabaseHelper(Context context, Handler widgetHostResetHandler) {
+            this(context, widgetHostResetHandler, LauncherFiles.LAUNCHER_DB);
             // Table creation sometimes fails silently, which leads to a crash loop.
             // This way, we will try to create a table every time after crash, so the device
             // would eventually be able to recover.
-            if (!tableExists(TABLE_FAVORITES) || !tableExists(TABLE_WORKSPACE_SCREENS)) {
+            if (!tableExists(Favorites.TABLE_NAME) || !tableExists(WorkspaceScreens.TABLE_NAME)) {
                 Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
                 // This operation is a no-op if the table already exists.
                 addFavoritesTable(getWritableDatabase(), true);
                 addWorkspacesTable(getWritableDatabase(), true);
             }
 
+            initIds();
+        }
+
+        /**
+         * Constructor used in tests and for restore.
+         */
+        public DatabaseHelper(
+                Context context, Handler widgetHostResetHandler, String tableName) {
+            super(new NoLocaleSqliteContext(context), tableName, null, DATABASE_VERSION);
+            mContext = context;
+            mWidgetHostResetHandler = widgetHostResetHandler;
+        }
+
+        protected void initIds() {
             // In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
             // the DB here
             if (mMaxItemId == -1) {
@@ -514,18 +615,6 @@
             }
         }
 
-        /**
-         * Constructor used only in tests.
-         */
-        public DatabaseHelper(Context context, String tableName) {
-            super(context, tableName, null, DATABASE_VERSION);
-            mContext = context;
-
-            mAppWidgetHost = null;
-            mMaxItemId = initializeMaxItemId(getWritableDatabase());
-            mMaxScreenId = initializeMaxScreenId(getWritableDatabase());
-        }
-
         private boolean tableExists(String tableName) {
             Cursor c = getReadableDatabase().query(
                     true, "sqlite_master", new String[] {"tbl_name"},
@@ -538,42 +627,16 @@
             }
         }
 
-        public boolean wasNewDbCreated() {
-            return mNewDbCreated;
-        }
-
         @Override
         public void onCreate(SQLiteDatabase db) {
             if (LOGD) Log.d(TAG, "creating new launcher database");
 
             mMaxItemId = 1;
             mMaxScreenId = 0;
-            mNewDbCreated = true;
 
             addFavoritesTable(db, false);
             addWorkspacesTable(db, false);
 
-            // Database was just created, so wipe any previous widgets
-            if (mAppWidgetHost != null) {
-                mAppWidgetHost.deleteHost();
-
-                /**
-                 * Send notification that we've deleted the {@link AppWidgetHost},
-                 * probably as part of the initial database creation. The receiver may
-                 * want to re-call {@link AppWidgetHost#startListening()} to ensure
-                 * callbacks are correctly set.
-                 */
-                new MainThreadExecutor().execute(new Runnable() {
-
-                    @Override
-                    public void run() {
-                        if (mListener != null) {
-                            mListener.onAppWidgetHostReset();
-                        }
-                    }
-                });
-            }
-
             // Fresh and clean launcher DB.
             mMaxItemId = initializeMaxItemId(db);
             onEmptyDbCreated();
@@ -583,6 +646,16 @@
          * Overriden in tests.
          */
         protected void onEmptyDbCreated() {
+            // Database was just created, so wipe any previous widgets
+            if (mWidgetHostResetHandler != null) {
+                new AppWidgetHost(mContext, Launcher.APPWIDGET_HOST_ID).deleteHost();
+                mWidgetHostResetHandler.sendMessage(Message.obtain(
+                        mWidgetHostResetHandler,
+                        ChangeListenerWrapper.MSG_APP_WIDGET_HOST_RESET,
+                        mContext
+                ));
+            }
+
             // Set the flag for empty DB
             Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit();
 
@@ -591,44 +664,18 @@
                     mContext);
         }
 
-        protected long getDefaultUserSerial() {
+        public long getDefaultUserSerial() {
             return UserManagerCompat.getInstance(mContext).getSerialNumberForUser(
                     UserHandleCompat.myUserHandle());
         }
 
         private void addFavoritesTable(SQLiteDatabase db, boolean optional) {
-            String ifNotExists = optional ? " IF NOT EXISTS " : "";
-            db.execSQL("CREATE TABLE " + ifNotExists + TABLE_FAVORITES + " (" +
-                    "_id INTEGER PRIMARY KEY," +
-                    "title TEXT," +
-                    "intent TEXT," +
-                    "container INTEGER," +
-                    "screen INTEGER," +
-                    "cellX INTEGER," +
-                    "cellY INTEGER," +
-                    "spanX INTEGER," +
-                    "spanY INTEGER," +
-                    "itemType INTEGER," +
-                    "appWidgetId INTEGER NOT NULL DEFAULT -1," +
-                    "isShortcut INTEGER," +
-                    "iconType INTEGER," +
-                    "iconPackage TEXT," +
-                    "iconResource TEXT," +
-                    "icon BLOB," +
-                    "uri TEXT," +
-                    "displayMode INTEGER," +
-                    "appWidgetProvider TEXT," +
-                    "modified INTEGER NOT NULL DEFAULT 0," +
-                    "restored INTEGER NOT NULL DEFAULT 0," +
-                    "profileId INTEGER DEFAULT " + getDefaultUserSerial() + "," +
-                    "rank INTEGER NOT NULL DEFAULT 0," +
-                    "options INTEGER NOT NULL DEFAULT 0" +
-                    ");");
+            Favorites.addTableToDb(db, getDefaultUserSerial(), optional);
         }
 
         private void addWorkspacesTable(SQLiteDatabase db, boolean optional) {
             String ifNotExists = optional ? " IF NOT EXISTS " : "";
-            db.execSQL("CREATE TABLE " + ifNotExists + TABLE_WORKSPACE_SCREENS + " (" +
+            db.execSQL("CREATE TABLE " + ifNotExists + WorkspaceScreens.TABLE_NAME + " (" +
                     LauncherSettings.WorkspaceScreens._ID + " INTEGER PRIMARY KEY," +
                     LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," +
                     LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" +
@@ -639,10 +686,10 @@
             // Delete items directly on the workspace who's screen id doesn't exist
             //  "DELETE FROM favorites WHERE screen NOT IN (SELECT _id FROM workspaceScreens)
             //   AND container = -100"
-            String removeOrphanedDesktopItems = "DELETE FROM " + TABLE_FAVORITES +
+            String removeOrphanedDesktopItems = "DELETE FROM " + Favorites.TABLE_NAME +
                     " WHERE " +
                     LauncherSettings.Favorites.SCREEN + " NOT IN (SELECT " +
-                    LauncherSettings.WorkspaceScreens._ID + " FROM " + TABLE_WORKSPACE_SCREENS + ")" +
+                    LauncherSettings.WorkspaceScreens._ID + " FROM " + WorkspaceScreens.TABLE_NAME + ")" +
                     " AND " +
                     LauncherSettings.Favorites.CONTAINER + " = " +
                     LauncherSettings.Favorites.CONTAINER_DESKTOP;
@@ -651,7 +698,7 @@
             // Delete items contained in folders which no longer exist (after above statement)
             //  "DELETE FROM favorites  WHERE container <> -100 AND container <> -101 AND container
             //   NOT IN (SELECT _id FROM favorites WHERE itemType = 2)"
-            String removeOrphanedFolderItems = "DELETE FROM " + TABLE_FAVORITES +
+            String removeOrphanedFolderItems = "DELETE FROM " + Favorites.TABLE_NAME +
                     " WHERE " +
                     LauncherSettings.Favorites.CONTAINER + " <> " +
                     LauncherSettings.Favorites.CONTAINER_DESKTOP +
@@ -660,16 +707,12 @@
                     LauncherSettings.Favorites.CONTAINER_HOTSEAT +
                     " AND "
                     + LauncherSettings.Favorites.CONTAINER + " NOT IN (SELECT " +
-                    LauncherSettings.Favorites._ID + " FROM " + TABLE_FAVORITES +
+                    LauncherSettings.Favorites._ID + " FROM " + Favorites.TABLE_NAME +
                     " WHERE " + LauncherSettings.Favorites.ITEM_TYPE + " = " +
                     LauncherSettings.Favorites.ITEM_TYPE_FOLDER + ")";
             db.execSQL(removeOrphanedFolderItems);
         }
 
-        private void setFlagJustLoadedOldDb() {
-            Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, false).commit();
-        }
-
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
             if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion);
@@ -766,7 +809,13 @@
                     ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mContext);
                 case 25:
                     convertShortcutsToLauncherActivities(db);
-                case 26: {
+                case 26:
+                    // QSB was moved to the grid. Clear the first row on screen 0.
+                    if (FeatureFlags.QSB_ON_FIRST_SCREEN &&
+                            !LauncherDbUtils.prepareScreenZeroToHostQsb(db)) {
+                        break;
+                    }
+                case 27: {
                     // DB Upgraded successfully
                     return;
                 }
@@ -789,8 +838,8 @@
          * Clears all the data for a fresh start.
          */
         public void createEmptyDB(SQLiteDatabase db) {
-            db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
-            db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
+            db.execSQL("DROP TABLE IF EXISTS " + Favorites.TABLE_NAME);
+            db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME);
             onCreate(db);
         }
 
@@ -805,9 +854,8 @@
 
             try {
                 // Only consider the primary user as other users can't have a shortcut.
-                long userSerial = UserManagerCompat.getInstance(mContext)
-                        .getSerialNumberForUser(UserHandleCompat.myUserHandle());
-                c = db.query(TABLE_FAVORITES, new String[] {
+                long userSerial = getDefaultUserSerial();
+                c = db.query(Favorites.TABLE_NAME, new String[] {
                         Favorites._ID,
                         Favorites.INTENT,
                     }, "itemType=" + Favorites.ITEM_TYPE_SHORTCUT + " AND profileId=" + userSerial,
@@ -857,7 +905,7 @@
         public boolean recreateWorkspaceTable(SQLiteDatabase db) {
             db.beginTransaction();
             try {
-                Cursor c = db.query(TABLE_WORKSPACE_SCREENS,
+                Cursor c = db.query(WorkspaceScreens.TABLE_NAME,
                         new String[] {LauncherSettings.WorkspaceScreens._ID},
                         null, null, null, null,
                         LauncherSettings.WorkspaceScreens.SCREEN_RANK);
@@ -875,7 +923,7 @@
                     c.close();
                 }
 
-                db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
+                db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME);
                 addWorkspacesTable(db, false);
 
                 // Add all screen ids back
@@ -885,7 +933,7 @@
                     values.put(LauncherSettings.WorkspaceScreens._ID, sortedIDs.get(i));
                     values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
                     addModifiedTime(values);
-                    db.insertOrThrow(TABLE_WORKSPACE_SCREENS, null, values);
+                    db.insertOrThrow(WorkspaceScreens.TABLE_NAME, null, values);
                 }
                 db.setTransactionSuccessful();
                 mMaxScreenId = maxId;
@@ -932,12 +980,7 @@
         }
 
         private boolean addProfileColumn(SQLiteDatabase db) {
-            UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
-            // Default to the serial number of this user, for older
-            // shortcuts.
-            long userSerialNumber = userManager.getSerialNumberForUser(
-                    UserHandleCompat.myUserHandle());
-            return addIntegerColumn(db, Favorites.PROFILE_ID, userSerialNumber);
+            return addIntegerColumn(db, Favorites.PROFILE_ID, getDefaultUserSerial());
         }
 
         private boolean addIntegerColumn(SQLiteDatabase db, String columnName, long defaultValue) {
@@ -971,12 +1014,12 @@
 
         @Override
         public long insertAndCheck(SQLiteDatabase db, ContentValues values) {
-            return dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
+            return dbInsertAndCheck(this, db, Favorites.TABLE_NAME, null, values);
         }
 
         public void checkId(String table, ContentValues values) {
             long id = values.getAsLong(LauncherSettings.BaseLauncherColumns._ID);
-            if (table == LauncherProvider.TABLE_WORKSPACE_SCREENS) {
+            if (table == WorkspaceScreens.TABLE_NAME) {
                 mMaxScreenId = Math.max(id, mMaxScreenId);
             }  else {
                 mMaxItemId = Math.max(id, mMaxItemId);
@@ -984,7 +1027,7 @@
         }
 
         private long initializeMaxItemId(SQLiteDatabase db) {
-            return getMaxId(db, TABLE_FAVORITES);
+            return getMaxId(db, Favorites.TABLE_NAME);
         }
 
         // Generates a new ID to use for an workspace screen in your database. This method
@@ -1001,94 +1044,7 @@
         }
 
         private long initializeMaxScreenId(SQLiteDatabase db) {
-            return getMaxId(db, TABLE_WORKSPACE_SCREENS);
-        }
-
-        @Thunk boolean initializeExternalAdd(ContentValues values) {
-            // 1. Ensure that externally added items have a valid item id
-            long id = generateNewItemId();
-            values.put(LauncherSettings.Favorites._ID, id);
-
-            // 2. In the case of an app widget, and if no app widget id is specified, we
-            // attempt allocate and bind the widget.
-            Integer itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE);
-            if (itemType != null &&
-                    itemType.intValue() == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET &&
-                    !values.containsKey(LauncherSettings.Favorites.APPWIDGET_ID)) {
-
-                final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
-                ComponentName cn = ComponentName.unflattenFromString(
-                        values.getAsString(Favorites.APPWIDGET_PROVIDER));
-
-                if (cn != null) {
-                    try {
-                        int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
-                        values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
-                        if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) {
-                            return false;
-                        }
-                    } catch (RuntimeException e) {
-                        Log.e(TAG, "Failed to initialize external widget", e);
-                        return false;
-                    }
-                } else {
-                    return false;
-                }
-            }
-
-            // Add screen id if not present
-            long screenId = values.getAsLong(LauncherSettings.Favorites.SCREEN);
-            if (!addScreenIdIfNecessary(screenId)) {
-                return false;
-            }
-            return true;
-        }
-
-        // Returns true of screen id exists, or if successfully added
-        private boolean addScreenIdIfNecessary(long screenId) {
-            if (!hasScreenId(screenId)) {
-                int rank = getMaxScreenRank() + 1;
-
-                ContentValues v = new ContentValues();
-                v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
-                v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, rank);
-                if (dbInsertAndCheck(this, getWritableDatabase(),
-                        TABLE_WORKSPACE_SCREENS, null, v) < 0) {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        private boolean hasScreenId(long screenId) {
-            SQLiteDatabase db = getWritableDatabase();
-            Cursor c = db.rawQuery("SELECT * FROM " + TABLE_WORKSPACE_SCREENS + " WHERE "
-                    + LauncherSettings.WorkspaceScreens._ID + " = " + screenId, null);
-            if (c != null) {
-                int count = c.getCount();
-                c.close();
-                return count > 0;
-            } else {
-                return false;
-            }
-        }
-
-        private int getMaxScreenRank() {
-            SQLiteDatabase db = getWritableDatabase();
-            Cursor c = db.rawQuery("SELECT MAX(" + LauncherSettings.WorkspaceScreens.SCREEN_RANK
-                    + ") FROM " + TABLE_WORKSPACE_SCREENS, null);
-
-            // get the result
-            final int maxRankIndex = 0;
-            int rank = -1;
-            if (c != null && c.moveToNext()) {
-                rank = c.getInt(maxRankIndex);
-            }
-            if (c != null) {
-                c.close();
-            }
-
-            return rank;
+            return getMaxId(db, WorkspaceScreens.TABLE_NAME);
         }
 
         @Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
@@ -1104,7 +1060,7 @@
                 values.clear();
                 values.put(LauncherSettings.WorkspaceScreens._ID, id);
                 values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, rank);
-                if (dbInsertAndCheck(this, db, TABLE_WORKSPACE_SCREENS, null, values) < 0) {
+                if (dbInsertAndCheck(this, db, WorkspaceScreens.TABLE_NAME, null, values) < 0) {
                     throw new RuntimeException("Failed initialize screen table"
                             + "from default layout");
                 }
@@ -1117,301 +1073,6 @@
 
             return count;
         }
-
-        @Thunk void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) {
-            final ContentResolver resolver = mContext.getContentResolver();
-            Cursor c = null;
-            int count = 0;
-            int curScreen = 0;
-
-            try {
-                c = resolver.query(uri, null, null, null, "title ASC");
-            } catch (Exception e) {
-                // Ignore
-            }
-
-            // We already have a favorites database in the old provider
-            if (c != null) {
-                try {
-                    if (c.getCount() > 0) {
-                        final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
-                        final int intentIndex
-                                = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
-                        final int titleIndex
-                                = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
-                        final int iconTypeIndex
-                                = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
-                        final int iconIndex
-                                = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
-                        final int iconPackageIndex
-                                = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
-                        final int iconResourceIndex
-                                = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
-                        final int containerIndex
-                                = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
-                        final int itemTypeIndex
-                                = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
-                        final int screenIndex
-                                = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
-                        final int cellXIndex
-                                = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
-                        final int cellYIndex
-                                = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
-                        final int uriIndex
-                                = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
-                        final int displayModeIndex
-                                = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
-                        final int profileIndex
-                                = c.getColumnIndex(LauncherSettings.Favorites.PROFILE_ID);
-
-                        int i = 0;
-                        int curX = 0;
-                        int curY = 0;
-
-                        final LauncherAppState app = LauncherAppState.getInstance();
-                        final InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
-                        final int width = (int) profile.numColumns;
-                        final int height = (int) profile.numRows;
-                        final int hotseatWidth = (int) profile.numHotseatIcons;
-
-                        final HashSet<String> seenIntents = new HashSet<String>(c.getCount());
-
-                        final ArrayList<ContentValues> shortcuts = new ArrayList<ContentValues>();
-                        final ArrayList<ContentValues> folders = new ArrayList<ContentValues>();
-                        final SparseArray<ContentValues> hotseat = new SparseArray<ContentValues>();
-
-                        while (c.moveToNext()) {
-                            final int itemType = c.getInt(itemTypeIndex);
-                            if (itemType != Favorites.ITEM_TYPE_APPLICATION
-                                    && itemType != Favorites.ITEM_TYPE_SHORTCUT
-                                    && itemType != Favorites.ITEM_TYPE_FOLDER) {
-                                continue;
-                            }
-
-                            final int cellX = c.getInt(cellXIndex);
-                            final int cellY = c.getInt(cellYIndex);
-                            final int screen = c.getInt(screenIndex);
-                            int container = c.getInt(containerIndex);
-                            final String intentStr = c.getString(intentIndex);
-
-                            UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
-                            UserHandleCompat userHandle;
-                            final long userSerialNumber;
-                            if (profileIndex != -1 && !c.isNull(profileIndex)) {
-                                userSerialNumber = c.getInt(profileIndex);
-                                userHandle = userManager.getUserForSerialNumber(userSerialNumber);
-                            } else {
-                                // Default to the serial number of this user, for older
-                                // shortcuts.
-                                userHandle = UserHandleCompat.myUserHandle();
-                                userSerialNumber = userManager.getSerialNumberForUser(userHandle);
-                            }
-
-                            if (userHandle == null) {
-                                Launcher.addDumpLog(TAG, "skipping deleted user", true);
-                                continue;
-                            }
-
-                            Launcher.addDumpLog(TAG, "migrating \""
-                                + c.getString(titleIndex) + "\" ("
-                                + cellX + "," + cellY + "@"
-                                + LauncherSettings.Favorites.containerToString(container)
-                                + "/" + screen
-                                + "): " + intentStr, true);
-
-                            if (itemType != Favorites.ITEM_TYPE_FOLDER) {
-
-                                final Intent intent;
-                                final ComponentName cn;
-                                try {
-                                    intent = Intent.parseUri(intentStr, 0);
-                                } catch (URISyntaxException e) {
-                                    // bogus intent?
-                                    Launcher.addDumpLog(TAG,
-                                            "skipping invalid intent uri", true);
-                                    continue;
-                                }
-
-                                cn = intent.getComponent();
-                                if (TextUtils.isEmpty(intentStr)) {
-                                    // no intent? no icon
-                                    Launcher.addDumpLog(TAG, "skipping empty intent", true);
-                                    continue;
-                                } else if (cn != null &&
-                                        !LauncherModel.isValidPackageActivity(mContext, cn,
-                                                userHandle)) {
-                                    // component no longer exists.
-                                    Launcher.addDumpLog(TAG, "skipping item whose component " +
-                                            "no longer exists.", true);
-                                    continue;
-                                } else if (container ==
-                                        LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                                    // Dedupe icons directly on the workspace
-
-                                    // Canonicalize
-                                    // the Play Store sets the package parameter, but Launcher
-                                    // 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;
-                                    } else {
-                                        seenIntents.add(key);
-                                    }
-                                }
-                            }
-
-                            ContentValues values = new ContentValues(c.getColumnCount());
-                            values.put(LauncherSettings.Favorites._ID, c.getInt(idIndex));
-                            values.put(LauncherSettings.Favorites.INTENT, intentStr);
-                            values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
-                            values.put(LauncherSettings.Favorites.ICON_TYPE,
-                                    c.getInt(iconTypeIndex));
-                            values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
-                            values.put(LauncherSettings.Favorites.ICON_PACKAGE,
-                                    c.getString(iconPackageIndex));
-                            values.put(LauncherSettings.Favorites.ICON_RESOURCE,
-                                    c.getString(iconResourceIndex));
-                            values.put(LauncherSettings.Favorites.ITEM_TYPE, itemType);
-                            values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
-                            values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
-                            values.put(LauncherSettings.Favorites.DISPLAY_MODE,
-                                    c.getInt(displayModeIndex));
-                            values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber);
-
-                            if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
-                                hotseat.put(screen, values);
-                            }
-
-                            if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                                // In a folder or in the hotseat, preserve position
-                                values.put(LauncherSettings.Favorites.SCREEN, screen);
-                                values.put(LauncherSettings.Favorites.CELLX, cellX);
-                                values.put(LauncherSettings.Favorites.CELLY, cellY);
-                            } else {
-                                // For items contained directly on one of the workspace screen,
-                                // we'll determine their location (screen, x, y) in a second pass.
-                            }
-
-                            values.put(LauncherSettings.Favorites.CONTAINER, container);
-
-                            if (itemType != Favorites.ITEM_TYPE_FOLDER) {
-                                shortcuts.add(values);
-                            } else {
-                                folders.add(values);
-                            }
-                        }
-
-                        // Now that we have all the hotseat icons, let's go through them left-right
-                        // and assign valid locations for them in the new hotseat
-                        final int N = hotseat.size();
-                        for (int idx=0; idx<N; idx++) {
-                            int hotseatX = hotseat.keyAt(idx);
-                            ContentValues values = hotseat.valueAt(idx);
-
-                            if (hotseatX == profile.hotseatAllAppsRank) {
-                                // let's drop this in the next available hole in the hotseat
-                                while (++hotseatX < hotseatWidth) {
-                                    if (hotseat.get(hotseatX) == null) {
-                                        // found a spot! move it here
-                                        values.put(LauncherSettings.Favorites.SCREEN,
-                                                hotseatX);
-                                        break;
-                                    }
-                                }
-                            }
-                            if (hotseatX >= hotseatWidth) {
-                                // no room for you in the hotseat? it's off to the desktop with you
-                                values.put(LauncherSettings.Favorites.CONTAINER,
-                                           Favorites.CONTAINER_DESKTOP);
-                            }
-                        }
-
-                        final ArrayList<ContentValues> allItems = new ArrayList<ContentValues>();
-                        // Folders first
-                        allItems.addAll(folders);
-                        // Then shortcuts
-                        allItems.addAll(shortcuts);
-
-                        // Layout all the folders
-                        for (ContentValues values: allItems) {
-                            if (values.getAsInteger(LauncherSettings.Favorites.CONTAINER) !=
-                                    LauncherSettings.Favorites.CONTAINER_DESKTOP) {
-                                // Hotseat items and folder items have already had their
-                                // location information set. Nothing to be done here.
-                                continue;
-                            }
-                            values.put(LauncherSettings.Favorites.SCREEN, curScreen);
-                            values.put(LauncherSettings.Favorites.CELLX, curX);
-                            values.put(LauncherSettings.Favorites.CELLY, curY);
-                            curX = (curX + 1) % width;
-                            if (curX == 0) {
-                                curY = (curY + 1);
-                            }
-                            // Leave the last row of icons blank on every screen
-                            if (curY == height - 1) {
-                                curScreen = (int) generateNewScreenId();
-                                curY = 0;
-                            }
-                        }
-
-                        if (allItems.size() > 0) {
-                            db.beginTransaction();
-                            try {
-                                for (ContentValues row: allItems) {
-                                    if (row == null) continue;
-                                    if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, row)
-                                            < 0) {
-                                        return;
-                                    } else {
-                                        count++;
-                                    }
-                                }
-                                db.setTransactionSuccessful();
-                            } finally {
-                                db.endTransaction();
-                            }
-                        }
-
-                        db.beginTransaction();
-                        try {
-                            for (i=0; i<=curScreen; i++) {
-                                final ContentValues values = new ContentValues();
-                                values.put(LauncherSettings.WorkspaceScreens._ID, i);
-                                values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
-                                if (dbInsertAndCheck(this, db, TABLE_WORKSPACE_SCREENS, null, values)
-                                        < 0) {
-                                    return;
-                                }
-                            }
-                            db.setTransactionSuccessful();
-                        } finally {
-                            db.endTransaction();
-                        }
-
-                        updateFolderItemsRank(db, false);
-                    }
-                } finally {
-                    c.close();
-                }
-            }
-
-            Launcher.addDumpLog(TAG, "migrated " + count + " icons from Launcher2 into "
-                    + (curScreen+1) + " screens", true);
-
-            // ensure that new screens are created to hold these icons
-            setFlagJustLoadedOldDb();
-
-            // Update max IDs; very important since we just grabbed IDs from another database
-            mMaxItemId = initializeMaxItemId(db);
-            mMaxScreenId = initializeMaxScreenId(db);
-            if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId + " mMaxScreenId: " + mMaxScreenId);
-        }
     }
 
     /**
@@ -1466,4 +1127,35 @@
             }
         }
     }
+
+    private static class ChangeListenerWrapper implements Handler.Callback {
+
+        private static final int MSG_LAUNCHER_PROVIDER_CHANGED = 1;
+        private static final int MSG_EXTRACTED_COLORS_CHANGED = 2;
+        private static final int MSG_APP_WIDGET_HOST_RESET = 3;
+
+        private LauncherProviderChangeListener mListener;
+
+        @Override
+        public boolean handleMessage(Message msg) {
+            if (mListener != null) {
+                switch (msg.what) {
+                    case MSG_LAUNCHER_PROVIDER_CHANGED:
+                        mListener.onLauncherProviderChange();
+                        break;
+                    case MSG_EXTRACTED_COLORS_CHANGED:
+                        mListener.onExtractedColorsChanged();
+                        break;
+                    case MSG_APP_WIDGET_HOST_RESET:
+                        Context context = (Context) msg.obj;
+                        if (context != null) {
+                            context.sendBroadcast(new Intent(Launcher.ACTION_APPWIDGET_HOST_RESET)
+                                    .setPackage(context.getPackageName()));
+                        }
+                        break;
+                }
+            }
+            return true;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java
index 1b78e9c..5998dad 100644
--- a/src/com/android/launcher3/LauncherProviderChangeListener.java
+++ b/src/com/android/launcher3/LauncherProviderChangeListener.java
@@ -9,7 +9,5 @@
 
     public void onLauncherProviderChange();
 
-    public void onSettingsChanged(String settings, boolean value);
-
-    public void onAppWidgetHostReset();
+    public void onExtractedColorsChanged();
 }
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 55a512f..643d48a 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -9,11 +9,16 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewDebug;
 
 public class LauncherRootView extends InsettableFrameLayout {
 
     private final Paint mOpaquePaint;
-    private boolean mDrawRightInsetBar;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private boolean mDrawSideInsetBar;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private int mLeftInsetBarWidth;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private int mRightInsetBarWidth;
 
     private View mAlignedView;
@@ -39,13 +44,15 @@
     @TargetApi(23)
     @Override
     protected boolean fitSystemWindows(Rect insets) {
-        mDrawRightInsetBar = insets.right > 0 &&
+        boolean rawInsetsChanged = !mInsets.equals(insets);
+        mDrawSideInsetBar = (insets.right > 0 || insets.left > 0) &&
                 (!Utilities.ATLEAST_MARSHMALLOW ||
                 getContext().getSystemService(ActivityManager.class).isLowRamDevice());
         mRightInsetBarWidth = insets.right;
-        setInsets(mDrawRightInsetBar ? new Rect(0, insets.top, 0, insets.bottom) : insets);
+        mLeftInsetBarWidth = insets.left;
+        setInsets(mDrawSideInsetBar ? new Rect(0, insets.top, 0, insets.bottom) : insets);
 
-        if (mAlignedView != null && mDrawRightInsetBar) {
+        if (mAlignedView != null && mDrawSideInsetBar) {
             // Apply margins on aligned view to handle left/right insets.
             MarginLayoutParams lp = (MarginLayoutParams) mAlignedView.getLayoutParams();
             if (lp.leftMargin != insets.left || lp.rightMargin != insets.right) {
@@ -55,6 +62,12 @@
             }
         }
 
+        if (rawInsetsChanged) {
+            // Update the grid again
+            Launcher launcher = Launcher.getLauncher(getContext());
+            launcher.onInsetsChanged(insets);
+        }
+
         return true; // I'll take it from here
     }
 
@@ -63,9 +76,14 @@
         super.dispatchDraw(canvas);
 
         // If the right inset is opaque, draw a black rectangle to ensure that is stays opaque.
-        if (mDrawRightInsetBar) {
-            int width = getWidth();
-            canvas.drawRect(width - mRightInsetBarWidth, 0, width, getHeight(), mOpaquePaint);
+        if (mDrawSideInsetBar) {
+            if (mRightInsetBarWidth > 0) {
+                int width = getWidth();
+                canvas.drawRect(width - mRightInsetBarWidth, 0, width, getHeight(), mOpaquePaint);
+            }
+            if (mLeftInsetBarWidth > 0) {
+                canvas.drawRect(0, 0, mLeftInsetBarWidth, getHeight(), mOpaquePaint);
+            }
         }
     }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 55e6395..e884393 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -16,7 +16,10 @@
 
 package com.android.launcher3;
 
+import android.content.ContentResolver;
+import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
+import android.os.Bundle;
 import android.provider.BaseColumns;
 
 import com.android.launcher3.config.ProviderConfig;
@@ -67,35 +70,19 @@
         public static final int ITEM_TYPE_SHORTCUT = 1;
 
         /**
-         * The icon type.
-         * <P>Type: INTEGER</P>
-         */
-        public static final String ICON_TYPE = "iconType";
-
-        /**
-         * The icon is a resource identified by a package name and an integer id.
-         */
-        public static final int ICON_TYPE_RESOURCE = 0;
-
-        /**
-         * The icon is a bitmap.
-         */
-        public static final int ICON_TYPE_BITMAP = 1;
-
-        /**
-         * The icon package name, if icon type is ICON_TYPE_RESOURCE.
+         * The icon package name in Intent.ShortcutIconResource
          * <P>Type: TEXT</P>
          */
         public static final String ICON_PACKAGE = "iconPackage";
 
         /**
-         * The icon resource id, if icon type is ICON_TYPE_RESOURCE.
+         * The icon resource name in Intent.ShortcutIconResource
          * <P>Type: TEXT</P>
          */
         public static final String ICON_RESOURCE = "iconResource";
 
         /**
-         * The custom icon bitmap, if icon type is ICON_TYPE_BITMAP.
+         * The custom icon bitmap.
          * <P>Type: BLOB</P>
          */
         public static final String ICON = "icon";
@@ -214,16 +201,6 @@
         public static final int ITEM_TYPE_FOLDER = 2;
 
         /**
-        * The favorite is a live folder
-        *
-        * Note: live folders can no longer be added to Launcher, and any live folders which
-        * exist within the launcher database will be ignored when loading.  That said, these
-        * entries in the database may still exist, and are not automatically stripped.
-        */
-        @Deprecated
-        static final int ITEM_TYPE_LIVE_FOLDER = 3;
-
-        /**
          * The favorite is a widget
          */
         public static final int ITEM_TYPE_APPWIDGET = 4;
@@ -234,22 +211,9 @@
         public static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5;
 
         /**
-         * The favorite is a clock
+         * The gesture is an application created deep shortcut
          */
-        @Deprecated
-        static final int ITEM_TYPE_WIDGET_CLOCK = 1000;
-
-        /**
-         * The favorite is a search widget
-         */
-        @Deprecated
-        static final int ITEM_TYPE_WIDGET_SEARCH = 1001;
-
-        /**
-         * The favorite is a photo frame
-         */
-        @Deprecated
-        static final int ITEM_TYPE_WIDGET_PHOTO_FRAME = 1002;
+        public static final int ITEM_TYPE_DEEP_SHORTCUT = 6;
 
         /**
          * The appWidgetId of the widget
@@ -264,33 +228,6 @@
          * <P>Type: STRING</P>
          */
         public static final String APPWIDGET_PROVIDER = "appWidgetProvider";
-        
-        /**
-         * Indicates whether this favorite is an application-created shortcut or not.
-         * If the value is 0, the favorite is not an application-created shortcut, if the
-         * value is 1, it is an application-created shortcut.
-         * <P>Type: INTEGER</P>
-         */
-        @Deprecated
-        static final String IS_SHORTCUT = "isShortcut";
-
-        /**
-         * The URI associated with the favorite. It is used, for instance, by
-         * live folders to find the content provider.
-         * <P>Type: TEXT</P>
-         */
-        @Deprecated
-        static final String URI = "uri";
-
-        /**
-         * The display mode if the item is a live folder.
-         * <P>Type: INTEGER</P>
-         *
-         * @see android.provider.LiveFolders#DISPLAY_MODE_GRID
-         * @see android.provider.LiveFolders#DISPLAY_MODE_LIST
-         */
-        @Deprecated
-        static final String DISPLAY_MODE = "displayMode";
 
         /**
          * Boolean indicating that his item was restored and not yet successfully bound.
@@ -309,6 +246,32 @@
          * <p>Type: INTEGER</p>
          */
         public static final String OPTIONS = "options";
+
+        public static void addTableToDb(SQLiteDatabase db, long myProfileId, boolean optional) {
+            String ifNotExists = optional ? " IF NOT EXISTS " : "";
+            db.execSQL("CREATE TABLE " + ifNotExists + TABLE_NAME + " (" +
+                    "_id INTEGER PRIMARY KEY," +
+                    "title TEXT," +
+                    "intent TEXT," +
+                    "container INTEGER," +
+                    "screen INTEGER," +
+                    "cellX INTEGER," +
+                    "cellY INTEGER," +
+                    "spanX INTEGER," +
+                    "spanY INTEGER," +
+                    "itemType INTEGER," +
+                    "appWidgetId INTEGER NOT NULL DEFAULT -1," +
+                    "iconPackage TEXT," +
+                    "iconResource TEXT," +
+                    "icon BLOB," +
+                    "appWidgetProvider TEXT," +
+                    "modified INTEGER NOT NULL DEFAULT 0," +
+                    "restored INTEGER NOT NULL DEFAULT 0," +
+                    "profileId INTEGER DEFAULT " + myProfileId + "," +
+                    "rank INTEGER NOT NULL DEFAULT 0," +
+                    "options INTEGER NOT NULL DEFAULT 0" +
+                    ");");
+        }
     }
 
     /**
@@ -319,13 +282,28 @@
         public static final Uri CONTENT_URI = Uri.parse("content://" +
                 ProviderConfig.AUTHORITY + "/settings");
 
-        public static final String METHOD_GET_BOOLEAN = "get_boolean_setting";
-        public static final String METHOD_SET_BOOLEAN = "set_boolean_setting";
+        public static final String METHOD_CLEAR_EMPTY_DB_FLAG = "clear_empty_db_flag";
+        public static final String METHOD_WAS_EMPTY_DB_CREATED = "get_empty_db_flag";
+
+        public static final String METHOD_DELETE_EMPTY_FOLDERS = "delete_empty_folders";
+
+        public static final String METHOD_NEW_ITEM_ID = "generate_new_item_id";
+        public static final String METHOD_NEW_SCREEN_ID = "generate_new_screen_id";
+
+        public static final String METHOD_CREATE_EMPTY_DB = "create_empty_db";
+        public static final String METHOD_DELETE_DB = "delete_db";
+
+        public static final String METHOD_LOAD_DEFAULT_FAVORITES = "load_default_favorites";
+
+        public static final String METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID =
+                "set_extracted_colors_and_wallpaper_id_setting";
+        public static final String EXTRA_EXTRACTED_COLORS = "extra_extractedColors";
+        public static final String EXTRA_WALLPAPER_ID = "extra_wallpaperId";
 
         public static final String EXTRA_VALUE = "value";
-        public static final String EXTRA_DEFAULT_VALUE = "default_value";
 
-        // Extra for set_boolean method to also notify the backup manager of the change.
-        public static final String NOTIFY_BACKUP = "notify_backup";
+        public static Bundle call(ContentResolver cr, String method) {
+            return cr.call(CONTENT_URI, method, null, null);
+        }
     }
 }
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index b95e2b0..eb70650 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -24,14 +24,18 @@
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
 import android.content.res.Resources;
+import android.os.Build;
 import android.util.Log;
 import android.view.View;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 
 import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.util.UiThreadCircularReveal;
+import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.CircleRevealOutlineProvider;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.WidgetsContainerView;
 
@@ -80,6 +84,17 @@
  */
 public class LauncherStateTransitionAnimation {
 
+    /**
+     * animation used for all apps and widget tray when
+     *{@link FeatureFlags#LAUNCHER3_ALL_APPS_PULL_UP} is {@code false}
+     */
+    public static final int CIRCULAR_REVEAL = 0;
+    /**
+     * animation used for all apps and not widget tray when
+     *{@link FeatureFlags#LAUNCHER3_ALL_APPS_PULL_UP} is {@code true}
+     */
+    public static final int PULLUP = 1;
+
     private static final float FINAL_REVEAL_ALPHA_FOR_WIDGETS = 0.3f;
 
     /**
@@ -111,9 +126,11 @@
 
     @Thunk Launcher mLauncher;
     @Thunk AnimatorSet mCurrentAnimation;
+    AllAppsTransitionController mAllAppsController;
 
-    public LauncherStateTransitionAnimation(Launcher l) {
+    public LauncherStateTransitionAnimation(Launcher l, AllAppsTransitionController allAppsController) {
         mLauncher = l;
+        mAllAppsController = allAppsController;
     }
 
     /**
@@ -125,7 +142,7 @@
     public void startAnimationToAllApps(final Workspace.State fromWorkspaceState,
             final boolean animated, final boolean startSearchAfterTransition) {
         final AllAppsContainerView toView = mLauncher.getAppsView();
-        final View buttonView = mLauncher.getAllAppsButton();
+        final View buttonView = mLauncher.getStartViewForAllAppsRevealAnimation();
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
             @Override
             public float getMaterialRevealViewStartFinalRadius() {
@@ -146,14 +163,19 @@
             }
             @Override
             void onTransitionComplete() {
+                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
                 if (startSearchAfterTransition) {
                     toView.startAppsSearch();
                 }
             }
         };
+        int animType = CIRCULAR_REVEAL;
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+            animType = PULLUP;
+        }
         // Only animate the search bar if animating from spring loaded mode back to all apps
-        mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
-                Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, cb);
+        startAnimationToOverlay(fromWorkspaceState,
+                Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, animType, cb);
     }
 
     /**
@@ -163,29 +185,42 @@
             final boolean animated) {
         final WidgetsContainerView toView = mLauncher.getWidgetsView();
         final View buttonView = mLauncher.getWidgetsButton();
-
-        mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
-                Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated,
-                new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS));
+        startAnimationToOverlay(fromWorkspaceState,
+                Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated, CIRCULAR_REVEAL,
+                new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS){
+                    @Override
+                    void onTransitionComplete() {
+                        mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+                    }
+                });
     }
 
     /**
-     * Starts and animation to the workspace from the current overlay view.
+     * Starts an animation to the workspace from the current overlay view.
      */
     public void startAnimationToWorkspace(final Launcher.State fromState,
             final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
-            final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
+            final boolean animated, final Runnable onCompleteRunnable) {
         if (toWorkspaceState != Workspace.State.NORMAL &&
                 toWorkspaceState != Workspace.State.SPRING_LOADED &&
                 toWorkspaceState != Workspace.State.OVERVIEW) {
             Log.e(TAG, "Unexpected call to startAnimationToWorkspace");
         }
 
-        if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
-            startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState, toWorkspacePage,
+        if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED
+                || mAllAppsController.isTransitioning()) {
+            int animType = CIRCULAR_REVEAL;
+            if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+                animType = PULLUP;
+            }
+            startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState,
+                    animated, animType, onCompleteRunnable);
+        } else if (fromState == Launcher.State.WIDGETS ||
+                fromState == Launcher.State.WIDGETS_SPRING_LOADED) {
+            startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState,
                     animated, onCompleteRunnable);
         } else {
-            startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState, toWorkspacePage,
+            startAnimationToNewWorkspaceState(fromWorkspaceState, toWorkspaceState,
                     animated, onCompleteRunnable);
         }
     }
@@ -194,14 +229,16 @@
      * Creates and starts a new animation to a particular overlay view.
      */
     @SuppressLint("NewApi")
-    private AnimatorSet startAnimationToOverlay(
+    private void startAnimationToOverlay(
             final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
             final View buttonView, final BaseContainerView toView,
-            final boolean animated, final PrivateTransitionCallbacks pCb) {
+            final boolean animated, int animType, final PrivateTransitionCallbacks pCb) {
         final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
         final Resources res = mLauncher.getResources();
         final boolean material = Utilities.ATLEAST_LOLLIPOP;
         final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
+        final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime);
+
         final int itemsAlphaStagger = res.getInteger(R.integer.config_overlayItemsAlphaStagger);
 
         final View fromView = mLauncher.getWorkspace();
@@ -214,20 +251,34 @@
         // Cancel the current animation
         cancelAnimation();
 
-        // Create the workspace animation.
-        // NOTE: this call apparently also sets the state for the workspace if !animated
-        Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1,
-                animated, layerViews);
-
-        // Animate the search bar
-        startWorkspaceSearchBarAnimation(
-                toWorkspaceState, animated ? revealDuration : 0, animation);
-
-        Animator updateTransitionStepAnim = dispatchOnLauncherTransitionStepAnim(fromView, toView);
-
         final View contentView = toView.getContentView();
+        playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+                animated, initialized, animation, layerViews);
+        if (!animated || !initialized) {
+            if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
+                    toWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
+                mAllAppsController.finishPullUp();
+            }
+            toView.setTranslationX(0.0f);
+            toView.setTranslationY(0.0f);
+            toView.setScaleX(1.0f);
+            toView.setScaleY(1.0f);
+            toView.setAlpha(1.0f);
+            toView.setVisibility(View.VISIBLE);
 
-        if (animated && initialized) {
+            // Show the content view
+            contentView.setVisibility(View.VISIBLE);
+
+            dispatchOnLauncherTransitionPrepare(fromView, animated, false);
+            dispatchOnLauncherTransitionStart(fromView, animated, false);
+            dispatchOnLauncherTransitionEnd(fromView, animated, false);
+            dispatchOnLauncherTransitionPrepare(toView, animated, false);
+            dispatchOnLauncherTransitionStart(toView, animated, false);
+            dispatchOnLauncherTransitionEnd(toView, animated, false);
+            pCb.onTransitionComplete();
+            return;
+        }
+        if (animType == CIRCULAR_REVEAL) {
             // Setup the reveal view animation
             final View revealView = toView.getRevealView();
 
@@ -257,11 +308,11 @@
 
             // Create the animators
             PropertyValuesHolder panelAlpha =
-                    PropertyValuesHolder.ofFloat("alpha", revealViewToAlpha, 1f);
+                    PropertyValuesHolder.ofFloat(View.ALPHA, revealViewToAlpha, 1f);
             PropertyValuesHolder panelDriftY =
-                    PropertyValuesHolder.ofFloat("translationY", revealViewToYDrift, 0);
+                    PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, revealViewToYDrift, 0);
             PropertyValuesHolder panelDriftX =
-                    PropertyValuesHolder.ofFloat("translationX", revealViewToXDrift, 0);
+                    PropertyValuesHolder.ofFloat(View.TRANSLATION_X, revealViewToXDrift, 0);
             ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
                     panelAlpha, panelDriftY, panelDriftX);
             panelAlphaAndDrift.setDuration(revealDuration);
@@ -295,8 +346,8 @@
                 float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
                 AnimatorListenerAdapter listener = pCb.getMaterialRevealViewAnimatorListener(
                         revealView, buttonView);
-                Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
-                        height / 2, startRadius, revealRadius);
+                Animator reveal = new CircleRevealOutlineProvider(width / 2, height / 2,
+                        startRadius, revealRadius).createRevealAnimator(revealView);
                 reveal.setDuration(revealDuration);
                 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
                 if (listener != null) {
@@ -328,13 +379,6 @@
 
             });
 
-            // Play the workspace animation
-            if (workspaceAnim != null) {
-                animation.play(workspaceAnim);
-            }
-
-            animation.play(updateTransitionStepAnim);
-
             // Dispatch the prepare transition signal
             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
             dispatchOnLauncherTransitionPrepare(toView, animated, false);
@@ -354,7 +398,7 @@
                         if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
                             v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                         }
-                        if (Utilities.ATLEAST_LOLLIPOP && Utilities.isViewAttachedToWindow(v)) {
+                        if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
                             v.buildLayer();
                         }
                     }
@@ -368,28 +412,86 @@
             toView.bringToFront();
             toView.setVisibility(View.VISIBLE);
             toView.post(startAnimRunnable);
+            mCurrentAnimation = animation;
+        } else if (animType == PULLUP) {
+            // We are animating the content view alpha, so ensure we have a layer for it
+            layerViews.put(contentView, BUILD_AND_SET_LAYER);
 
-            return animation;
-        } else {
-            toView.setTranslationX(0.0f);
-            toView.setTranslationY(0.0f);
-            toView.setScaleX(1.0f);
-            toView.setScaleY(1.0f);
-            toView.setVisibility(View.VISIBLE);
-            toView.bringToFront();
+            animation.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    dispatchOnLauncherTransitionEnd(fromView, animated, false);
+                    dispatchOnLauncherTransitionEnd(toView, animated, false);
 
-            // Show the content view
-            contentView.setVisibility(View.VISIBLE);
+                    // Disable all necessary layers
+                    for (View v : layerViews.keySet()) {
+                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                            v.setLayerType(View.LAYER_TYPE_NONE, null);
+                        }
+                    }
+
+                    cleanupAnimation();
+                    pCb.onTransitionComplete();
+                }
+            });
+            boolean shouldPost = mAllAppsController.animateToAllApps(animation, revealDurationSlide);
 
             dispatchOnLauncherTransitionPrepare(fromView, animated, false);
-            dispatchOnLauncherTransitionStart(fromView, animated, false);
-            dispatchOnLauncherTransitionEnd(fromView, animated, false);
             dispatchOnLauncherTransitionPrepare(toView, animated, false);
-            dispatchOnLauncherTransitionStart(toView, animated, false);
-            dispatchOnLauncherTransitionEnd(toView, animated, false);
-            pCb.onTransitionComplete();
 
-            return null;
+            final AnimatorSet stateAnimation = animation;
+            final Runnable startAnimRunnable = new Runnable() {
+                public void run() {
+                    // Check that mCurrentAnimation hasn't changed while
+                    // we waited for a layout/draw pass
+                    if (mCurrentAnimation != stateAnimation)
+                        return;
+
+                    dispatchOnLauncherTransitionStart(fromView, animated, false);
+                    dispatchOnLauncherTransitionStart(toView, animated, false);
+
+                    // Enable all necessary layers
+                    for (View v : layerViews.keySet()) {
+                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                            v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                        }
+                        if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
+                            v.buildLayer();
+                        }
+                    }
+
+                    toView.requestFocus();
+                    stateAnimation.start();
+                }
+            };
+            mCurrentAnimation = animation;
+            if (shouldPost) {
+                toView.post(startAnimRunnable);
+            } else {
+                startAnimRunnable.run();
+            }
+        }
+    }
+
+    /**
+     * Plays animations used by various transitions.
+     */
+    private void playCommonTransitionAnimations(
+            Workspace.State toWorkspaceState, View fromView, View toView,
+            boolean animated, boolean initialized, AnimatorSet animation,
+            HashMap<View, Integer> layerViews) {
+        // Create the workspace animation.
+        // NOTE: this call apparently also sets the state for the workspace if !animated
+        Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
+                animated, layerViews);
+
+        if (animated && initialized) {
+            // Play the workspace animation
+            if (workspaceAnim != null) {
+                animation.play(workspaceAnim);
+            }
+            // Dispatch onLauncherTransitionStep() as the animation interpolates.
+            animation.play(dispatchOnLauncherTransitionStepAnim(fromView, toView));
         }
     }
 
@@ -412,11 +514,11 @@
     }
 
     /**
-     * Starts and animation to the workspace from the apps view.
+     * Starts an animation to the workspace from the apps view.
      */
     private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState,
-            final Workspace.State toWorkspaceState, final int toWorkspacePage,
-            final boolean animated, final Runnable onCompleteRunnable) {
+            final Workspace.State toWorkspaceState, final boolean animated, int type,
+            final Runnable onCompleteRunnable) {
         AllAppsContainerView appsView = mLauncher.getAppsView();
         // No alpha anim from all apps
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
@@ -444,19 +546,23 @@
                     }
                 };
             }
+            @Override
+            void onTransitionComplete() {
+                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+            }
         };
         // Only animate the search bar if animating to spring loaded mode from all apps
-        mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
-                toWorkspacePage, mLauncher.getAllAppsButton(), appsView,
-                animated, onCompleteRunnable, cb);
+        startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
+                mLauncher.getStartViewForAllAppsRevealAnimation(), appsView,
+                animated, type, onCompleteRunnable, cb);
     }
 
     /**
-     * Starts and animation to the workspace from the widgets view.
+     * Starts an animation to the workspace from the widgets view.
      */
     private void startAnimationToWorkspaceFromWidgets(final Workspace.State fromWorkspaceState,
-            final Workspace.State toWorkspaceState, final int toWorkspacePage,
-            final boolean animated, final Runnable onCompleteRunnable) {
+            final Workspace.State toWorkspaceState, final boolean animated,
+            final Runnable onCompleteRunnable) {
         final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
         PrivateTransitionCallbacks cb =
                 new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS) {
@@ -470,30 +576,119 @@
                     }
                 };
             }
+            @Override
+            void onTransitionComplete() {
+                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+            }
         };
-        mCurrentAnimation = startAnimationToWorkspaceFromOverlay(
+        startAnimationToWorkspaceFromOverlay(
                 fromWorkspaceState, toWorkspaceState,
-                toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView,
-                animated, onCompleteRunnable, cb);
+                mLauncher.getWidgetsButton(), widgetsView,
+                animated, CIRCULAR_REVEAL, onCompleteRunnable, cb);
+    }
+
+    /**
+     * Starts an animation to the workspace from another workspace state, e.g. normal to overview.
+     */
+    private void startAnimationToNewWorkspaceState(final Workspace.State fromWorkspaceState,
+            final Workspace.State toWorkspaceState, final boolean animated,
+            final Runnable onCompleteRunnable) {
+        final View fromWorkspace = mLauncher.getWorkspace();
+        final HashMap<View, Integer> layerViews = new HashMap<>();
+        final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
+        final int revealDuration = mLauncher.getResources()
+                .getInteger(R.integer.config_overlayRevealTime);
+
+        // Cancel the current animation
+        cancelAnimation();
+
+        boolean multiplePagesVisible = toWorkspaceState.hasMultipleVisiblePages;
+
+        playCommonTransitionAnimations(toWorkspaceState, fromWorkspace, null,
+                animated, animated, animation, layerViews);
+
+        if (animated) {
+            dispatchOnLauncherTransitionPrepare(fromWorkspace, animated, multiplePagesVisible);
+
+            final AnimatorSet stateAnimation = animation;
+            final Runnable startAnimRunnable = new Runnable() {
+                @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+                public void run() {
+                    // Check that mCurrentAnimation hasn't changed while
+                    // we waited for a layout/draw pass
+                    if (mCurrentAnimation != stateAnimation)
+                        return;
+
+                    dispatchOnLauncherTransitionStart(fromWorkspace, animated, true);
+
+                    // Enable all necessary layers
+                    for (View v : layerViews.keySet()) {
+                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                            v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                        }
+                        if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
+                            v.buildLayer();
+                        }
+                    }
+                    stateAnimation.start();
+                }
+            };
+            animation.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    dispatchOnLauncherTransitionEnd(fromWorkspace, animated, true);
+
+                    // Run any queued runnables
+                    if (onCompleteRunnable != null) {
+                        onCompleteRunnable.run();
+                    }
+
+                    // Disable all necessary layers
+                    for (View v : layerViews.keySet()) {
+                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                            v.setLayerType(View.LAYER_TYPE_NONE, null);
+                        }
+                    }
+
+                    // This can hold unnecessary references to views.
+                    cleanupAnimation();
+                }
+            });
+            fromWorkspace.post(startAnimRunnable);
+            mCurrentAnimation = animation;
+        } else /* if (!animated) */ {
+            dispatchOnLauncherTransitionPrepare(fromWorkspace, animated, multiplePagesVisible);
+            dispatchOnLauncherTransitionStart(fromWorkspace, animated, true);
+            dispatchOnLauncherTransitionEnd(fromWorkspace, animated, true);
+
+            // Run any queued runnables
+            if (onCompleteRunnable != null) {
+                onCompleteRunnable.run();
+            }
+
+            mCurrentAnimation = null;
+        }
     }
 
     /**
      * Creates and starts a new animation to the workspace.
      */
-    private AnimatorSet startAnimationToWorkspaceFromOverlay(
+    private void startAnimationToWorkspaceFromOverlay(
             final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
-            final int toWorkspacePage,
             final View buttonView, final BaseContainerView fromView,
-            final boolean animated, final Runnable onCompleteRunnable,
+            final boolean animated, int animType, final Runnable onCompleteRunnable,
             final PrivateTransitionCallbacks pCb) {
         final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
         final Resources res = mLauncher.getResources();
         final boolean material = Utilities.ATLEAST_LOLLIPOP;
         final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
+        final int revealDurationSlide = res.getInteger(R.integer.config_overlaySlideRevealTime);
         final int itemsAlphaStagger =
                 res.getInteger(R.integer.config_overlayItemsAlphaStagger);
 
         final View toView = mLauncher.getWorkspace();
+        final View revealView = fromView.getRevealView();
+        final View contentView = fromView.getContentView();
 
         final HashMap<View, Integer> layerViews = new HashMap<>();
 
@@ -503,27 +698,31 @@
         // Cancel the current animation
         cancelAnimation();
 
-        // Create the workspace animation.
-        // NOTE: this call apparently also sets the state for the workspace if !animated
-        Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
-                toWorkspacePage, animated, layerViews);
+        boolean multiplePagesVisible = toWorkspaceState.hasMultipleVisiblePages;
 
-        // Animate the search bar
-        startWorkspaceSearchBarAnimation(
-                toWorkspaceState, animated ? revealDuration : 0, animation);
-
-        Animator updateTransitionStepAnim = dispatchOnLauncherTransitionStepAnim(fromView, toView);
-
-        if (animated && initialized) {
-            // Play the workspace animation
-            if (workspaceAnim != null) {
-                animation.play(workspaceAnim);
+        playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+                animated, initialized, animation, layerViews);
+        if (!animated || !initialized) {
+            if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
+                    fromWorkspaceState == Workspace.State.NORMAL_HIDDEN) {
+                mAllAppsController.finishPullDown();
             }
+            fromView.setVisibility(View.GONE);
+            dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible);
+            dispatchOnLauncherTransitionStart(fromView, animated, true);
+            dispatchOnLauncherTransitionEnd(fromView, animated, true);
+            dispatchOnLauncherTransitionPrepare(toView, animated, multiplePagesVisible);
+            dispatchOnLauncherTransitionStart(toView, animated, true);
+            dispatchOnLauncherTransitionEnd(toView, animated, true);
+            pCb.onTransitionComplete();
 
-            animation.play(updateTransitionStepAnim);
-            final View revealView = fromView.getRevealView();
-            final View contentView = fromView.getContentView();
-
+            // Run any queued runnables
+            if (onCompleteRunnable != null) {
+                onCompleteRunnable.run();
+            }
+            return;
+        }
+        if (animType == CIRCULAR_REVEAL) {
             // hideAppsCustomizeHelper is called in some cases when it is already hidden
             // don't perform all these no-op animations. In particularly, this was causing
             // the all-apps button to pop in and out.
@@ -599,13 +798,24 @@
                 itemsAlpha.setInterpolator(decelerateInterpolator);
                 animation.play(itemsAlpha);
 
+                // Invalidate the scrim throughout the animation to ensure the highlight
+                // cutout is correct throughout.
+                ValueAnimator invalidateScrim = ValueAnimator.ofFloat(0f, 1f);
+                invalidateScrim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                    @Override
+                    public void onAnimationUpdate(ValueAnimator animation) {
+                        mLauncher.getDragLayer().invalidateScrim();
+                    }
+                });
+                animation.play(invalidateScrim);
+
                 if (material) {
                     // Animate the all apps button
                     float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
                     AnimatorListenerAdapter listener =
                             pCb.getMaterialRevealViewAnimatorListener(revealView, buttonView);
-                    Animator reveal = UiThreadCircularReveal.createCircularReveal(revealView, width / 2,
-                            height / 2, revealRadius, finalRadius);
+                    Animator reveal = new CircleRevealOutlineProvider(width / 2, height / 2,
+                            revealRadius, finalRadius).createRevealAnimator(revealView);
                     reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
                     reveal.setDuration(revealDuration);
                     reveal.setStartDelay(itemsAlphaStagger);
@@ -616,8 +826,8 @@
                 }
             }
 
-            dispatchOnLauncherTransitionPrepare(fromView, animated, true);
-            dispatchOnLauncherTransitionPrepare(toView, animated, true);
+            dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible);
+            dispatchOnLauncherTransitionPrepare(toView, animated, multiplePagesVisible);
 
             animation.addListener(new AnimatorListenerAdapter() {
                 @Override
@@ -653,6 +863,7 @@
 
             final AnimatorSet stateAnimation = animation;
             final Runnable startAnimRunnable = new Runnable() {
+                @TargetApi(Build.VERSION_CODES.LOLLIPOP)
                 public void run() {
                     // Check that mCurrentAnimation hasn't changed while
                     // we waited for a layout/draw pass
@@ -667,52 +878,98 @@
                         if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
                             v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                         }
-                        if (Utilities.ATLEAST_LOLLIPOP && Utilities.isViewAttachedToWindow(v)) {
+                        if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
                             v.buildLayer();
                         }
                     }
                     stateAnimation.start();
                 }
             };
+            mCurrentAnimation = animation;
             fromView.post(startAnimRunnable);
+        } else if (animType == PULLUP) {
+            // We are animating the content view alpha, so ensure we have a layer for it
+            layerViews.put(contentView, BUILD_AND_SET_LAYER);
 
-            return animation;
-        } else {
-            fromView.setVisibility(View.GONE);
-            dispatchOnLauncherTransitionPrepare(fromView, animated, true);
-            dispatchOnLauncherTransitionStart(fromView, animated, true);
-            dispatchOnLauncherTransitionEnd(fromView, animated, true);
-            dispatchOnLauncherTransitionPrepare(toView, animated, true);
-            dispatchOnLauncherTransitionStart(toView, animated, true);
-            dispatchOnLauncherTransitionEnd(toView, animated, true);
-            pCb.onTransitionComplete();
+            animation.addListener(new AnimatorListenerAdapter() {
+                boolean canceled = false;
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    canceled = true;
+                }
 
-            // Run any queued runnables
-            if (onCompleteRunnable != null) {
-                onCompleteRunnable.run();
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (canceled) return;
+                    dispatchOnLauncherTransitionEnd(fromView, animated, true);
+                    dispatchOnLauncherTransitionEnd(toView, animated, true);
+                    // Run any queued runnables
+                    if (onCompleteRunnable != null) {
+                        onCompleteRunnable.run();
+                    }
+
+                    // Disable all necessary layers
+                    for (View v : layerViews.keySet()) {
+                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                            v.setLayerType(View.LAYER_TYPE_NONE, null);
+                        }
+                    }
+
+                    cleanupAnimation();
+                    pCb.onTransitionComplete();
+                }
+
+            });
+            boolean shouldPost = mAllAppsController.animateToWorkspace(animation, revealDurationSlide);
+
+            // Dispatch the prepare transition signal
+            dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible);
+            dispatchOnLauncherTransitionPrepare(toView, animated, multiplePagesVisible);
+
+            final AnimatorSet stateAnimation = animation;
+            final Runnable startAnimRunnable = new Runnable() {
+                public void run() {
+                    // Check that mCurrentAnimation hasn't changed while
+                    // we waited for a layout/draw pass
+                    if (mCurrentAnimation != stateAnimation)
+                        return;
+
+                    dispatchOnLauncherTransitionStart(fromView, animated, false);
+                    dispatchOnLauncherTransitionStart(toView, animated, false);
+
+                    // Enable all necessary layers
+                    for (View v : layerViews.keySet()) {
+                        if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
+                            v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+                        }
+                        if (Utilities.ATLEAST_LOLLIPOP && v.isAttachedToWindow()) {
+                            v.buildLayer();
+                        }
+                    }
+
+                    // Focus the new view
+                    toView.requestFocus();
+                    stateAnimation.start();
+                }
+            };
+            mCurrentAnimation = animation;
+            if (shouldPost) {
+                fromView.post(startAnimRunnable);
+            } else {
+                startAnimRunnable.run();
             }
-
-            return null;
         }
-    }
-
-    /**
-     * Coordinates the workspace search bar animation along with the launcher state animation.
-     */
-    private void startWorkspaceSearchBarAnimation(
-            final Workspace.State toWorkspaceState, int duration, AnimatorSet animation) {
-        final SearchDropTargetBar.State toSearchBarState =
-                toWorkspaceState.searchDropTargetBarState;
-        mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration, animation);
+        return;
     }
 
     /**
      * Dispatches the prepare-transition event to suitable views.
      */
-    void dispatchOnLauncherTransitionPrepare(View v, boolean animated, boolean toWorkspace) {
+    void dispatchOnLauncherTransitionPrepare(View v, boolean animated,
+            boolean multiplePagesVisible) {
         if (v instanceof LauncherTransitionable) {
             ((LauncherTransitionable) v).onLauncherTransitionPrepare(mLauncher, animated,
-                    toWorkspace);
+                    multiplePagesVisible);
         }
     }
 
diff --git a/src/com/android/launcher3/LauncherTransitionable.java b/src/com/android/launcher3/LauncherTransitionable.java
index 49af692..b97aaec 100644
--- a/src/com/android/launcher3/LauncherTransitionable.java
+++ b/src/com/android/launcher3/LauncherTransitionable.java
@@ -20,7 +20,7 @@
  * An interface to get callbacks during a launcher transition.
  */
 public interface LauncherTransitionable {
-    void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace);
+    void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean multiplePagesVisible);
     void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace);
     void onLauncherTransitionStep(Launcher l, float t);
     void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace);
diff --git a/src/com/android/launcher3/PageIndicator.java b/src/com/android/launcher3/PageIndicator.java
deleted file mode 100644
index 62ea03b..0000000
--- a/src/com/android/launcher3/PageIndicator.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.animation.LayoutTransition;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.widget.LinearLayout;
-
-import java.util.ArrayList;
-
-public class PageIndicator extends LinearLayout {
-    @SuppressWarnings("unused")
-    private static final String TAG = "PageIndicator";
-    // Want this to look good? Keep it odd
-    private static final boolean MODULATE_ALPHA_ENABLED = false;
-
-    private LayoutInflater mLayoutInflater;
-    private int[] mWindowRange = new int[2];
-    private int mMaxWindowSize;
-
-    private ArrayList<PageIndicatorMarker> mMarkers =
-            new ArrayList<PageIndicatorMarker>();
-    private int mActiveMarkerIndex;
-
-    public static class PageMarkerResources {
-        int activeId;
-        int inactiveId;
-
-        public PageMarkerResources() {
-            activeId = R.drawable.ic_pageindicator_current;
-            inactiveId = R.drawable.ic_pageindicator_default;
-        }
-        public PageMarkerResources(int aId, int iaId) {
-            activeId = aId;
-            inactiveId = iaId;
-        }
-    }
-
-    public PageIndicator(Context context) {
-        this(context, null);
-    }
-
-    public PageIndicator(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public PageIndicator(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.PageIndicator, defStyle, 0);
-        mMaxWindowSize = a.getInteger(R.styleable.PageIndicator_windowSize, 15);
-        mWindowRange[0] = 0;
-        mWindowRange[1] = 0;
-        mLayoutInflater = LayoutInflater.from(context);
-        a.recycle();
-
-        // Set the layout transition properties
-        LayoutTransition transition = getLayoutTransition();
-        transition.setDuration(175);
-    }
-
-    private void enableLayoutTransitions() {
-        LayoutTransition transition = getLayoutTransition();
-        transition.enableTransitionType(LayoutTransition.APPEARING);
-        transition.enableTransitionType(LayoutTransition.DISAPPEARING);
-        transition.enableTransitionType(LayoutTransition.CHANGE_APPEARING);
-        transition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
-    }
-
-    private void disableLayoutTransitions() {
-        LayoutTransition transition = getLayoutTransition();
-        transition.disableTransitionType(LayoutTransition.APPEARING);
-        transition.disableTransitionType(LayoutTransition.DISAPPEARING);
-        transition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
-        transition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
-    }
-
-    void offsetWindowCenterTo(int activeIndex, boolean allowAnimations) {
-        if (activeIndex < 0) {
-            new Throwable().printStackTrace();
-        }
-        int windowSize = Math.min(mMarkers.size(), mMaxWindowSize);
-        int hWindowSize = (int) windowSize / 2;
-        float hfWindowSize = windowSize / 2f;
-        int windowStart = Math.max(0, activeIndex - hWindowSize);
-        int windowEnd = Math.min(mMarkers.size(), windowStart + mMaxWindowSize);
-        windowStart = windowEnd - Math.min(mMarkers.size(), windowSize);
-        int windowMid = windowStart + (windowEnd - windowStart) / 2;
-        boolean windowAtStart = (windowStart == 0);
-        boolean windowAtEnd = (windowEnd == mMarkers.size());
-        boolean windowMoved = (mWindowRange[0] != windowStart) ||
-                (mWindowRange[1] != windowEnd);
-
-        if (!allowAnimations) {
-            disableLayoutTransitions();
-        }
-
-        // Remove all the previous children that are no longer in the window
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            PageIndicatorMarker marker = (PageIndicatorMarker) getChildAt(i);
-            int markerIndex = mMarkers.indexOf(marker);
-            if (markerIndex < windowStart || markerIndex >= windowEnd) {
-                removeView(marker);
-            }
-        }
-
-        // Add all the new children that belong in the window
-        for (int i = 0; i < mMarkers.size(); ++i) {
-            PageIndicatorMarker marker = (PageIndicatorMarker) mMarkers.get(i);
-            if (windowStart <= i && i < windowEnd) {
-                if (indexOfChild(marker) < 0) {
-                    addView(marker, i - windowStart);
-                }
-                if (i == activeIndex) {
-                    marker.activate(windowMoved);
-                } else {
-                    marker.inactivate(windowMoved);
-                }
-            } else {
-                marker.inactivate(true);
-            }
-
-            if (MODULATE_ALPHA_ENABLED) {
-                // Update the marker's alpha
-                float alpha = 1f;
-                if (mMarkers.size() > windowSize) {
-                    if ((windowAtStart && i > hWindowSize) ||
-                        (windowAtEnd && i < (mMarkers.size() - hWindowSize)) ||
-                        (!windowAtStart && !windowAtEnd)) {
-                        alpha = 1f - Math.abs((i - windowMid) / hfWindowSize);
-                    }
-                }
-                marker.animate().alpha(alpha).setDuration(500).start();
-            }
-        }
-
-        if (!allowAnimations) {
-            enableLayoutTransitions();
-        }
-
-        mWindowRange[0] = windowStart;
-        mWindowRange[1] = windowEnd;
-    }
-
-    void addMarker(int index, PageMarkerResources marker, boolean allowAnimations) {
-        index = Math.max(0, Math.min(index, mMarkers.size()));
-
-        PageIndicatorMarker m =
-            (PageIndicatorMarker) mLayoutInflater.inflate(R.layout.page_indicator_marker,
-                    this, false);
-        m.setMarkerDrawables(marker.activeId, marker.inactiveId);
-
-        mMarkers.add(index, m);
-        offsetWindowCenterTo(mActiveMarkerIndex, allowAnimations);
-    }
-    void addMarkers(ArrayList<PageMarkerResources> markers, boolean allowAnimations) {
-        for (int i = 0; i < markers.size(); ++i) {
-            addMarker(Integer.MAX_VALUE, markers.get(i), allowAnimations);
-        }
-    }
-
-    void updateMarker(int index, PageMarkerResources marker) {
-        PageIndicatorMarker m = mMarkers.get(index);
-        m.setMarkerDrawables(marker.activeId, marker.inactiveId);
-    }
-
-    void removeMarker(int index, boolean allowAnimations) {
-        if (mMarkers.size() > 0) {
-            index = Math.max(0, Math.min(mMarkers.size() - 1, index));
-            mMarkers.remove(index);
-            offsetWindowCenterTo(mActiveMarkerIndex, allowAnimations);
-        }
-    }
-    void removeAllMarkers(boolean allowAnimations) {
-        while (mMarkers.size() > 0) {
-            removeMarker(Integer.MAX_VALUE, allowAnimations);
-        }
-    }
-
-    void setActiveMarker(int index) {
-        // Center the active marker
-        mActiveMarkerIndex = index;
-        offsetWindowCenterTo(index, false);
-    }
-
-    void dumpState(String txt) {
-        System.out.println(txt);
-        System.out.println("\tmMarkers: " + mMarkers.size());
-        for (int i = 0; i < mMarkers.size(); ++i) {
-            PageIndicatorMarker m = mMarkers.get(i);
-            System.out.println("\t\t(" + i + ") " + m);
-        }
-        System.out.println("\twindow: [" + mWindowRange[0] + ", " + mWindowRange[1] + "]");
-        System.out.println("\tchildren: " + getChildCount());
-        for (int i = 0; i < getChildCount(); ++i) {
-            PageIndicatorMarker m = (PageIndicatorMarker) getChildAt(i);
-            System.out.println("\t\t(" + i + ") " + m);
-        }
-        System.out.println("\tactive: " + mActiveMarkerIndex);
-    }
-}
diff --git a/src/com/android/launcher3/PageIndicatorMarker.java b/src/com/android/launcher3/PageIndicatorMarker.java
deleted file mode 100644
index 7bf21dd..0000000
--- a/src/com/android/launcher3/PageIndicatorMarker.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-public class PageIndicatorMarker extends FrameLayout {
-    @SuppressWarnings("unused")
-    private static final String TAG = "PageIndicator";
-
-    private static final int MARKER_FADE_DURATION = 175;
-
-    private ImageView mActiveMarker;
-    private ImageView mInactiveMarker;
-    private boolean mIsActive = false;
-
-    public PageIndicatorMarker(Context context) {
-        this(context, null);
-    }
-
-    public PageIndicatorMarker(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public PageIndicatorMarker(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mActiveMarker = (ImageView) findViewById(R.id.active);
-        mInactiveMarker = (ImageView) findViewById(R.id.inactive);
-    }
-
-    void setMarkerDrawables(int activeResId, int inactiveResId) {
-        Resources r = getResources();
-        mActiveMarker.setImageDrawable(r.getDrawable(activeResId));
-        mInactiveMarker.setImageDrawable(r.getDrawable(inactiveResId));
-    }
-
-    void activate(boolean immediate) {
-        if (immediate) {
-            mActiveMarker.animate().cancel();
-            mActiveMarker.setAlpha(1f);
-            mActiveMarker.setScaleX(1f);
-            mActiveMarker.setScaleY(1f);
-            mInactiveMarker.animate().cancel();
-            mInactiveMarker.setAlpha(0f);
-        } else {
-            mActiveMarker.animate()
-                    .alpha(1f)
-                    .scaleX(1f)
-                    .scaleY(1f)
-                    .setDuration(MARKER_FADE_DURATION).start();
-            mInactiveMarker.animate()
-                    .alpha(0f)
-                    .setDuration(MARKER_FADE_DURATION).start();
-        }
-        mIsActive = true;
-    }
-
-    void inactivate(boolean immediate) {
-        if (immediate) {
-            mInactiveMarker.animate().cancel();
-            mInactiveMarker.setAlpha(1f);
-            mActiveMarker.animate().cancel();
-            mActiveMarker.setAlpha(0f);
-            mActiveMarker.setScaleX(0.5f);
-            mActiveMarker.setScaleY(0.5f);
-        } else {
-            mInactiveMarker.animate().alpha(1f)
-                    .setDuration(MARKER_FADE_DURATION).start();
-            mActiveMarker.animate()
-                    .alpha(0f)
-                    .scaleX(0.5f)
-                    .scaleY(0.5f)
-                    .setDuration(MARKER_FADE_DURATION).start();
-        }
-        mIsActive = false;
-    }
-
-    boolean isActive() {
-        return mIsActive;
-    }
-}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index b0e46f9..e380e26 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -18,10 +18,10 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
+import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -42,6 +42,7 @@
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.view.accessibility.AccessibilityEvent;
@@ -49,6 +50,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.Interpolator;
 
+import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.util.LauncherEdgeEffect;
 import com.android.launcher3.util.Thunk;
 
@@ -66,9 +68,8 @@
     // the min drag distance for a fling to register, to prevent random page shifts
     private static final int MIN_LENGTH_FOR_FLING = 25;
 
-    protected static final int PAGE_SNAP_ANIMATION_DURATION = 750;
+    public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
     protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
-    protected static final float NANOTIME_DIV = 1000000000.0f;
 
     private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
     // The page is moved more than halfway, automatically move to the next page on touch up.
@@ -92,17 +93,15 @@
     protected int mMinFlingVelocity;
     protected int mMinSnapVelocity;
 
-    protected float mDensity;
-    protected float mSmoothingTime;
-    protected float mTouchX;
-
     protected boolean mFirstLayout = true;
     private int mNormalChildHeight;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     protected int mCurrentPage;
     protected int mRestorePage = INVALID_RESTORE_PAGE;
-    protected int mChildCountOnLastLayout;
+    private int mChildCountOnLastLayout;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     protected int mNextPage = INVALID_PAGE;
     protected int mMaxScrollX;
     protected LauncherScroller mScroller;
@@ -116,10 +115,10 @@
     private float mDownMotionY;
     private float mDownScrollX;
     private float mDragViewBaselineLeft;
-    protected float mLastMotionX;
-    protected float mLastMotionXRemainder;
-    protected float mLastMotionY;
-    protected float mTotalMotionX;
+    private float mLastMotionX;
+    private float mLastMotionXRemainder;
+    private float mLastMotionY;
+    private float mTotalMotionX;
     private int mLastScreenCenter = -1;
 
     private boolean mCancelTap;
@@ -133,15 +132,12 @@
     protected final static int TOUCH_STATE_REORDERING = 4;
 
     protected int mTouchState = TOUCH_STATE_REST;
-    protected boolean mForceScreenScrolled = false;
+    private boolean mForceScreenScrolled = false;
 
     protected OnLongClickListener mLongClickListener;
 
     protected int mTouchSlop;
     private int mMaximumVelocity;
-    protected int mPageLayoutWidthGap;
-    protected int mPageLayoutHeightGap;
-    protected boolean mCenterPagesVertically;
     protected boolean mAllowOverScroll = true;
     protected int[] mTempVisiblePagesRange = new int[2];
 
@@ -149,20 +145,16 @@
 
     protected int mActivePointerId = INVALID_POINTER;
 
-    private PageSwitchListener mPageSwitchListener;
-
-    // If true, modify alpha of neighboring pages as user scrolls left/right
-    protected boolean mFadeInAdjacentScreens = false;
-
     protected boolean mIsPageMoving = false;
 
     protected boolean mWasInOverscroll = false;
 
     // Page Indicator
     @Thunk int mPageIndicatorViewId;
-    @Thunk PageIndicator mPageIndicator;
+    protected PageIndicator mPageIndicator;
     // The viewport whether the pages are to be contained (the actual view may be larger than the
     // viewport)
+    @ViewDebug.ExportedProperty(category = "launcher")
     private Rect mViewport = new Rect();
 
     // Reordering
@@ -174,7 +166,7 @@
 
     private float mMinScale = 1f;
     private boolean mUseMinScale = false;
-    protected View mDragView;
+    @Thunk View mDragView;
     private Runnable mSidePageHoverRunnable;
     @Thunk int mSidePageHoverIndex = -1;
     // This variable's scope is only for the duration of startReordering() and endReordering()
@@ -183,7 +175,7 @@
     // animation after endReordering()
     private boolean mIsReordering;
     // The runnable that settles the page after snapToPage and animateDragViewToOriginalPosition
-    private int NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT = 2;
+    private static final int NUM_ANIMATIONS_RUNNING_BEFORE_ZOOM_OUT = 2;
     private int mPostReorderingPreZoomInRemainingAnimationCount;
     private Runnable mPostReorderingPreZoomInRunnable;
 
@@ -201,10 +193,6 @@
     private final LauncherEdgeEffect mEdgeGlowLeft = new LauncherEdgeEffect();
     private final LauncherEdgeEffect mEdgeGlowRight = new LauncherEdgeEffect();
 
-    public interface PageSwitchListener {
-        void onPageSwitch(View newPage, int newPageIndex);
-    }
-
     public PagedView(Context context) {
         this(context, null);
     }
@@ -218,11 +206,6 @@
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.PagedView, defStyle, 0);
-
-        mPageLayoutWidthGap = a.getDimensionPixelSize(
-                R.styleable.PagedView_pageLayoutWidthGap, 0);
-        mPageLayoutHeightGap = a.getDimensionPixelSize(
-                R.styleable.PagedView_pageLayoutHeightGap, 0);
         mPageIndicatorViewId = a.getResourceId(R.styleable.PagedView_pageIndicator, -1);
         a.recycle();
 
@@ -238,16 +221,15 @@
         mScroller = new LauncherScroller(getContext());
         setDefaultInterpolator(new ScrollInterpolator());
         mCurrentPage = 0;
-        mCenterPagesVertically = true;
 
         final ViewConfiguration configuration = ViewConfiguration.get(getContext());
         mTouchSlop = configuration.getScaledPagingTouchSlop();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
-        mDensity = getResources().getDisplayMetrics().density;
 
-        mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
-        mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity);
-        mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity);
+        float density = getResources().getDisplayMetrics().density;
+        mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * density);
+        mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * density);
+        mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * density);
         setOnHierarchyChangeListener(this);
         setWillNotDraw(false);
     }
@@ -262,47 +244,14 @@
         mScroller.setInterpolator(mDefaultInterpolator);
     }
 
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        // Hook up the page indicator
-        ViewGroup parent = (ViewGroup) getParent();
-        ViewGroup grandParent = (ViewGroup) parent.getParent();
-        if (mPageIndicator == null && mPageIndicatorViewId > -1) {
-            mPageIndicator = (PageIndicator) grandParent.findViewById(mPageIndicatorViewId);
-            mPageIndicator.removeAllMarkers(true);
-
-            ArrayList<PageIndicator.PageMarkerResources> markers =
-                    new ArrayList<PageIndicator.PageMarkerResources>();
-            for (int i = 0; i < getChildCount(); ++i) {
-                markers.add(getPageIndicatorMarker(i));
-            }
-
-            mPageIndicator.addMarkers(markers, true);
-
-            OnClickListener listener = getPageIndicatorClickListener();
-            if (listener != null) {
-                mPageIndicator.setOnClickListener(listener);
-            }
+    public void initParentViews(View parent) {
+        if (mPageIndicatorViewId > -1) {
+            mPageIndicator = (PageIndicator) parent.findViewById(mPageIndicatorViewId);
+            mPageIndicator.setMarkersCount(getChildCount());
             mPageIndicator.setContentDescription(getPageIndicatorDescription());
         }
     }
 
-    protected String getPageIndicatorDescription() {
-        return getCurrentPageDescription();
-    }
-
-    protected OnClickListener getPageIndicatorClickListener() {
-        return null;
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        // Unhook the page indicator
-        mPageIndicator = null;
-    }
-
     // Convenience methods to map points from self to parent and vice versa
     private float[] mapPointFromViewToParent(View v, float x, float y) {
         sTmpPoint[0] = x;
@@ -355,7 +304,7 @@
     int getViewportWidth() {
         return mViewport.width();
     }
-    int getViewportHeight() {
+    public int getViewportHeight() {
         return mViewport.height();
     }
 
@@ -369,26 +318,15 @@
         return (getMeasuredHeight() - getViewportHeight()) / 2;
     }
 
-    PageIndicator getPageIndicator() {
+    public PageIndicator getPageIndicator() {
         return mPageIndicator;
     }
-    protected PageIndicator.PageMarkerResources getPageIndicatorMarker(int pageIndex) {
-        return new PageIndicator.PageMarkerResources();
-    }
 
     /**
-     * Add a page change listener which will be called when a page is _finished_ listening.
-     *
-     */
-    public void setPageSwitchListener(PageSwitchListener pageSwitchListener) {
-        mPageSwitchListener = pageSwitchListener;
-        if (mPageSwitchListener != null) {
-            mPageSwitchListener.onPageSwitch(getPageAt(mCurrentPage), mCurrentPage);
-        }
-    }
-
-    /**
-     * Returns the index of the currently displayed page.
+     * Returns the index of the currently displayed page. When in free scroll mode, this is the page
+     * that the user was on before entering free scroll mode (e.g. the home screen page they
+     * long-pressed on to enter the overview). Try using {@link #getPageNearestToCenterOfScreen()}
+     * to get the page the user is currently scrolling over.
      */
     public int getCurrentPage() {
         return mCurrentPage;
@@ -397,11 +335,11 @@
     /**
      * Returns the index of page to be shown immediately afterwards.
      */
-    int getNextPage() {
+    public int getNextPage() {
         return (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
     }
 
-    int getPageCount() {
+    public int getPageCount() {
         return getChildCount();
     }
 
@@ -426,7 +364,7 @@
         }
         scrollTo(newX, 0);
         mScroller.setFinalX(newX);
-        forceFinishScroller();
+        forceFinishScroller(true);
     }
 
     private void abortScrollerAnimation(boolean resetNextPage) {
@@ -438,11 +376,13 @@
         }
     }
 
-    private void forceFinishScroller() {
+    private void forceFinishScroller(boolean resetNextPage) {
         mScroller.forceFinished(true);
         // We need to clean up the next page here to avoid computeScrollHelper from
         // updating current page on the pass.
-        mNextPage = INVALID_PAGE;
+        if (resetNextPage) {
+            mNextPage = INVALID_PAGE;
+        }
     }
 
     private int validateNewPage(int newPage) {
@@ -454,7 +394,7 @@
                     Math.min(newPage, mTempVisiblePagesRange[1]));
         }
         // Ensure that it is clamped by the actual set of children in all cases
-        validatedPage = Math.max(0, Math.min(validatedPage, getPageCount() - 1));
+        validatedPage = Utilities.boundToRange(validatedPage, 0, getPageCount() - 1);
         return validatedPage;
     }
 
@@ -493,10 +433,6 @@
      * has settled.
      */
     protected void notifyPageSwitchListener() {
-        if (mPageSwitchListener != null) {
-            mPageSwitchListener.onPageSwitch(getPageAt(getNextPage()), getNextPage());
-        }
-
         updatePageIndicator();
     }
 
@@ -568,7 +504,7 @@
             // in the free scroll mode, we make sure to end the scroll operation.
             if (!mScroller.isFinished() &&
                     (x > mFreeScrollMaxScrollX || x < mFreeScrollMinScrollX)) {
-                forceFinishScroller();
+                forceFinishScroller(false);
             }
 
             x = Math.min(x, mFreeScrollMaxScrollX);
@@ -605,9 +541,6 @@
             super.scrollTo(x, y);
         }
 
-        mTouchX = x;
-        mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
-
         // Update the last motion events when scrolling
         if (isReordering(true)) {
             float[] p = mapPointFromParentToView(this, mParentDownMotionX, mParentDownMotionY);
@@ -637,17 +570,23 @@
 
     // we moved this functionality to a helper function so SmoothPagedView can reuse it
     protected boolean computeScrollHelper() {
+        return computeScrollHelper(true);
+    }
+
+    protected boolean computeScrollHelper(boolean shouldInvalidate) {
         if (mScroller.computeScrollOffset()) {
             // Don't bother scrolling if the page does not need to be moved
             if (getScrollX() != mScroller.getCurrX()
-                || getScrollY() != mScroller.getCurrY()) {
+                    || getScrollY() != mScroller.getCurrY()) {
                 float scaleX = mFreeScroll ? getScaleX() : 1f;
                 int scrollX = (int) (mScroller.getCurrX() * (1 / scaleX));
                 scrollTo(scrollX, mScroller.getCurrY());
             }
-            invalidate();
+            if (shouldInvalidate) {
+                invalidate();
+            }
             return true;
-        } else if (mNextPage != INVALID_PAGE) {
+        } else if (mNextPage != INVALID_PAGE && shouldInvalidate) {
             sendScrollAccessibilityEvent();
 
             mCurrentPage = validateNewPage(mNextPage);
@@ -680,6 +619,9 @@
     public static class LayoutParams extends ViewGroup.LayoutParams {
         public boolean isFullScreenPage = false;
 
+        // If true, the start edge of the page snaps to the start edge of the viewport.
+        public boolean matchStartEdge = false;
+
         /**
          * {@inheritDoc}
          */
@@ -835,6 +777,7 @@
         setMeasuredDimension(scaledWidthSize, scaledHeightSize);
     }
 
+    @SuppressLint("DrawAllocation")
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         if (getChildCount() == 0) {
@@ -873,9 +816,7 @@
                     childTop = offsetY;
                 } else {
                     childTop = offsetY + getPaddingTop() + mInsets.top;
-                    if (mCenterPagesVertically) {
-                        childTop += (getViewportHeight() - mInsets.top - mInsets.bottom - verticalPadding - child.getMeasuredHeight()) / 2;
-                    }
+                    childTop += (getViewportHeight() - mInsets.top - mInsets.bottom - verticalPadding - child.getMeasuredHeight()) / 2;
                 }
 
                 final int childWidth = child.getMeasuredWidth();
@@ -957,12 +898,16 @@
     }
 
     @Thunk void updateMaxScrollX() {
+        mMaxScrollX = computeMaxScrollX();
+    }
+
+    protected int computeMaxScrollX() {
         int childCount = getChildCount();
         if (childCount > 0) {
             final int index = mIsRtl ? 0 : childCount - 1;
-            mMaxScrollX = getScrollForPage(index);
+            return getScrollForPage(index);
         } else {
-            mMaxScrollX = 0;
+            return 0;
         }
     }
 
@@ -981,10 +926,7 @@
         // Update the page indicator, we don't update the page indicator as we
         // add/remove pages
         if (mPageIndicator != null && !isReordering(false)) {
-            int pageIndex = indexOfChild(child);
-            mPageIndicator.addMarker(pageIndex,
-                    getPageIndicatorMarker(pageIndex),
-                    true);
+            mPageIndicator.addMarker();
         }
 
         // This ensures that when children are added, they get the correct transforms / alphas
@@ -1001,11 +943,11 @@
         invalidate();
     }
 
-    private void removeMarkerForView(int index) {
+    private void removeMarkerForView() {
         // Update the page indicator, we don't update the page indicator as we
         // add/remove pages
         if (mPageIndicator != null && !isReordering(false)) {
-            mPageIndicator.removeMarker(index, true);
+            mPageIndicator.removeMarker();
         }
     }
 
@@ -1013,21 +955,21 @@
     public void removeView(View v) {
         // XXX: We should find a better way to hook into this before the view
         // gets removed form its parent...
-        removeMarkerForView(indexOfChild(v));
+        removeMarkerForView();
         super.removeView(v);
     }
     @Override
     public void removeViewInLayout(View v) {
         // XXX: We should find a better way to hook into this before the view
         // gets removed form its parent...
-        removeMarkerForView(indexOfChild(v));
+        removeMarkerForView();
         super.removeViewInLayout(v);
     }
     @Override
     public void removeViewAt(int index) {
         // XXX: We should find a better way to hook into this before the view
         // gets removed form its parent...
-        removeMarkerForView(index);
+        removeMarkerForView();
         super.removeViewAt(index);
     }
     @Override
@@ -1035,7 +977,7 @@
         // Update the page indicator, we don't update the page indicator as we
         // add/remove pages
         if (mPageIndicator != null) {
-            mPageIndicator.removeAllMarkers(true);
+            mPageIndicator.setMarkersCount(0);
         }
 
         super.removeAllViewsInLayout();
@@ -1082,7 +1024,6 @@
                         break;
                     }
                 }
-
                 curScreen = i;
                 if (range[0] < 0) {
                     range[0] = curScreen;
@@ -1457,10 +1398,10 @@
             mTotalMotionX += Math.abs(mLastMotionX - x);
             mLastMotionX = x;
             mLastMotionXRemainder = 0;
-            mTouchX = getViewportOffsetX() + getScrollX();
-            mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
             onScrollInteractionBegin();
             pageBeginMoving();
+            // Stop listening for things like pinches.
+            requestDisallowInterceptTouchEvent(true);
         }
     }
 
@@ -1563,6 +1504,7 @@
     }
 
     private void setEnableFreeScroll(boolean freeScroll) {
+        boolean wasFreeScroll = mFreeScroll;
         mFreeScroll = freeScroll;
 
         if (mFreeScroll) {
@@ -1573,6 +1515,8 @@
             } else if (getCurrentPage() > mTempVisiblePagesRange[1]) {
                 setCurrentPage(mTempVisiblePagesRange[1]);
             }
+        } else if (wasFreeScroll) {
+            snapToPage(getNextPage());
         }
 
         setEnableOverscroll(!freeScroll);
@@ -1657,8 +1601,6 @@
                 // keep the remainder because we are actually testing if we've moved from the last
                 // scrolled position (which is discrete).
                 if (Math.abs(deltaX) >= 1.0f) {
-                    mTouchX += deltaX;
-                    mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
                     scrollBy((int) deltaX, 0);
                     mLastMotionX = x;
                     mLastMotionXRemainder = deltaX - (int) deltaX;
@@ -1686,7 +1628,8 @@
                 if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY);
 
                 final int pageUnderPointIndex = getNearestHoverOverPageIndex();
-                if (pageUnderPointIndex > -1 && pageUnderPointIndex != indexOfChild(mDragView)) {
+                // Do not allow any page to be moved to 0th position.
+                if (pageUnderPointIndex > 0 && pageUnderPointIndex != indexOfChild(mDragView)) {
                     mTempVisiblePagesRange[0] = 0;
                     mTempVisiblePagesRange[1] = getPageCount() - 1;
                     getFreeScrollPageRange(mTempVisiblePagesRange);
@@ -1718,16 +1661,14 @@
 
                                     // Animate the view translation from its old position to its new
                                     // position
-                                    AnimatorSet anim = (AnimatorSet) v.getTag(ANIM_TAG_KEY);
+                                    ObjectAnimator anim = (ObjectAnimator) v.getTag();
                                     if (anim != null) {
                                         anim.cancel();
                                     }
 
                                     v.setTranslationX(oldX - newX);
-                                    anim = new AnimatorSet();
+                                    anim = LauncherAnimUtils.ofFloat(v, View.TRANSLATION_X, 0);
                                     anim.setDuration(REORDERING_REORDER_REPOSITION_DURATION);
-                                    anim.playTogether(
-                                            ObjectAnimator.ofFloat(v, "translationX", 0f));
                                     anim.start();
                                     v.setTag(anim);
                                 }
@@ -1809,6 +1750,7 @@
                     mScroller.setInterpolator(mDefaultInterpolator);
                     mScroller.fling(initialScrollX,
                             getScrollY(), vX, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
+                    mNextPage = getPageNearestToCenterOfScreen((int) (mScroller.getFinalX() / scaleX));
                     invalidate();
                 }
                 onScrollInteractionEnd();
@@ -1858,6 +1800,7 @@
         case MotionEvent.ACTION_CANCEL:
             if (mTouchState == TOUCH_STATE_SCROLLING) {
                 snapToDestination();
+                onScrollInteractionEnd();
             }
             resetTouchState();
             break;
@@ -1891,7 +1834,7 @@
     }
 
     protected void onUnhandledTap(MotionEvent ev) {
-        ((Launcher) getContext()).onClick(this);
+        Launcher.getLauncher(getContext()).onClick(this);
     }
 
     @Override
@@ -1969,9 +1912,13 @@
     }
 
     int getPageNearestToCenterOfScreen() {
+        return getPageNearestToCenterOfScreen(getScrollX());
+    }
+
+    private int getPageNearestToCenterOfScreen(int scaledScrollX) {
+        int screenCenter = getViewportOffsetX() + scaledScrollX + (getViewportWidth() / 2);
         int minDistanceFromScreenCenter = Integer.MAX_VALUE;
         int minDistanceFromScreenCenterIndex = -1;
-        int screenCenter = getViewportOffsetX() + getScrollX() + (getViewportWidth() / 2);
         final int childCount = getChildCount();
         for (int i = 0; i < childCount; ++i) {
             View layout = (View) getPageAt(i);
@@ -1991,7 +1938,7 @@
         snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
     }
 
-    private static class ScrollInterpolator implements Interpolator {
+    public static class ScrollInterpolator implements Interpolator {
         public ScrollInterpolator() {
         }
 
@@ -2049,7 +1996,7 @@
         snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
     }
 
-    protected void snapToPageImmediately(int whichPage) {
+    public void snapToPageImmediately(int whichPage) {
         snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true, null);
     }
 
@@ -2119,20 +2066,6 @@
         if (getNextPage() < getChildCount() -1) snapToPage(getNextPage() + 1);
     }
 
-    public int getPageForView(View v) {
-        int result = -1;
-        if (v != null) {
-            ViewParent vp = v.getParent();
-            int count = getChildCount();
-            for (int i = 0; i < count; i++) {
-                if (vp == getPageAt(i)) {
-                    return i;
-                }
-            }
-        }
-        return result;
-    }
-
     @Override
     public boolean performLongClick() {
         mCancelTap = true;
@@ -2172,13 +2105,12 @@
     // Animate the drag view back to the original position
     private void animateDragViewToOriginalPosition() {
         if (mDragView != null) {
-            AnimatorSet anim = new AnimatorSet();
-            anim.setDuration(REORDERING_DROP_REPOSITION_DURATION);
-            anim.playTogether(
-                    ObjectAnimator.ofFloat(mDragView, "translationX", 0f),
-                    ObjectAnimator.ofFloat(mDragView, "translationY", 0f),
-                    ObjectAnimator.ofFloat(mDragView, "scaleX", 1f),
-                    ObjectAnimator.ofFloat(mDragView, "scaleY", 1f));
+            Animator anim = new LauncherViewPropertyAnimator(mDragView)
+                    .translationX(0)
+                    .translationY(0)
+                    .scaleX(1)
+                    .scaleY(1)
+                    .setDuration(REORDERING_DROP_REPOSITION_DURATION);
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
@@ -2216,7 +2148,8 @@
     public boolean startReordering(View v) {
         int dragViewIndex = indexOfChild(v);
 
-        if (mTouchState != TOUCH_STATE_REST || dragViewIndex == -1) return false;
+        // Do not allow the first page to be moved around
+        if (mTouchState != TOUCH_STATE_REST || dragViewIndex <= 0) return false;
 
         mTempVisiblePagesRange[0] = 0;
         mTempVisiblePagesRange[1] = getPageCount() - 1;
@@ -2275,9 +2208,8 @@
         animateDragViewToOriginalPosition();
     }
 
-    private static final int ANIM_TAG_KEY = 100;
-
     /* Accessibility */
+    @SuppressWarnings("deprecation")
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
@@ -2336,8 +2268,12 @@
         return false;
     }
 
+    protected String getPageIndicatorDescription() {
+        return getCurrentPageDescription();
+    }
+
     protected String getCurrentPageDescription() {
-        return String.format(getContext().getString(R.string.default_scroll_format),
+        return getContext().getString(R.string.default_scroll_format,
                 getNextPage() + 1, getChildCount());
     }
 
diff --git a/src/com/android/launcher3/PendingAddItemInfo.java b/src/com/android/launcher3/PendingAddItemInfo.java
index 1aaf85b..31820d7 100644
--- a/src/com/android/launcher3/PendingAddItemInfo.java
+++ b/src/com/android/launcher3/PendingAddItemInfo.java
@@ -29,4 +29,9 @@
      * The component that will be created.
      */
     public ComponentName componentName;
+
+    @Override
+    protected String dumpProperties() {
+        return super.dumpProperties() + " componentName=" + componentName;
+    }
 }
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index 1c02904..f01c7f2 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -32,6 +32,7 @@
 import android.text.StaticLayout;
 import android.text.TextPaint;
 import android.util.TypedValue;
+import android.view.ContextThemeWrapper;
 import android.view.View;
 import android.view.View.OnClickListener;
 
@@ -63,9 +64,9 @@
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info,
             boolean disabledForSafeMode) {
-        super(context);
+        super(new ContextThemeWrapper(context, R.style.WidgetContainerTheme));
 
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
         mInfo = info;
         mStartState = info.restoreStatus;
         mIconLookupIntent = new Intent().setComponent(info.providerName);
@@ -189,9 +190,19 @@
         }
     }
 
+    /**
+     * A pending widget is ready for setup after the provider is installed and
+     *   1) Widget id is not valid: the widget id is not yet bound to the provider, probably
+     *                              because the launcher doesn't have appropriate permissions.
+     *                              Note that we would still have an allocated id as that does not
+     *                              require any permissions and can be done during view inflation.
+     *   2) UI is not ready: the id is valid and the bound. But the widget has a configure activity
+     *                       which needs to be called once.
+     */
     public boolean isReadyForClickSetup() {
-        return (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0
-                && (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0;
+        return !mInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
+                && (mInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
+                || mInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID));
     }
 
     private void updateDrawableBounds() {
diff --git a/src/com/android/launcher3/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java
new file mode 100644
index 0000000..baeb77c
--- /dev/null
+++ b/src/com/android/launcher3/PinchAnimationManager.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.util.Log;
+import android.view.View;
+import android.view.animation.LinearInterpolator;
+
+import static com.android.launcher3.Workspace.State.NORMAL;
+import static com.android.launcher3.Workspace.State.OVERVIEW;
+
+/**
+ * Manages the animations that play as the user pinches to/from overview mode.
+ *
+ *  It will look like this pinching in:
+ * - Workspace scales down
+ * - At some threshold 1, hotseat and QSB fade out (full animation)
+ * - At a later threshold 2, panel buttons fade in and scrim fades in
+ * - At a final threshold 3, snap to overview
+ *
+ * Pinching out:
+ * - Workspace scales up
+ * - At threshold 1, panel buttons fade out
+ * - At threshold 2, hotseat and QSB fade in and scrim fades out
+ * - At threshold 3, snap to workspace
+ *
+ * @see PinchToOverviewListener
+ * @see PinchThresholdManager
+ */
+public class PinchAnimationManager {
+    private static final String TAG = "PinchAnimationManager";
+
+    private static final int THRESHOLD_ANIM_DURATION = 150;
+    private static final LinearInterpolator INTERPOLATOR = new LinearInterpolator();
+
+    private static final int INDEX_HOTSEAT = 0;
+    private static final int INDEX_QSB = 1;
+    private static final int INDEX_OVERVIEW_PANEL_BUTTONS = 2;
+    private static final int INDEX_SCRIM = 3;
+
+    private final Animator[] mAnimators = new Animator[4];
+
+    private final int[] mVisiblePageRange = new int[2];
+    private Launcher mLauncher;
+    private Workspace mWorkspace;
+
+    private float mOverviewScale;
+    private float mOverviewTranslationY;
+    private int mNormalOverviewTransitionDuration;
+    private boolean mIsAnimating;
+
+    public PinchAnimationManager(Launcher launcher) {
+        mLauncher = launcher;
+        mWorkspace = launcher.mWorkspace;
+
+        mOverviewScale = mWorkspace.getOverviewModeShrinkFactor();
+        mOverviewTranslationY = mWorkspace.getOverviewModeTranslationY();
+        mNormalOverviewTransitionDuration = mWorkspace.getStateTransitionAnimation()
+                .mOverviewTransitionTime;
+    }
+
+    public int getNormalOverviewTransitionDuration() {
+        return mNormalOverviewTransitionDuration;
+    }
+
+    /**
+     * Interpolate from {@param currentProgress} to {@param toProgress}, calling
+     * {@link #setAnimationProgress(float)} throughout the duration. If duration is -1,
+     * the default overview transition duration is used.
+     */
+    public void animateToProgress(float currentProgress, float toProgress, int duration,
+            final PinchThresholdManager thresholdManager) {
+        if (duration == -1) {
+            duration = mNormalOverviewTransitionDuration;
+        }
+        ValueAnimator animator = ValueAnimator.ofFloat(currentProgress, toProgress);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    float pinchProgress = (Float) animation.getAnimatedValue();
+                    setAnimationProgress(pinchProgress);
+                    thresholdManager.updateAndAnimatePassedThreshold(pinchProgress,
+                            PinchAnimationManager.this);
+                }
+            }
+        );
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mIsAnimating = false;
+                thresholdManager.reset();
+                mWorkspace.onLauncherTransitionEnd(mLauncher, false, true);
+            }
+        });
+        animator.setDuration(duration).start();
+        mIsAnimating = true;
+    }
+
+    public boolean isAnimating() {
+        return mIsAnimating;
+    }
+
+    /**
+     * Animates to the specified progress. This should be called repeatedly throughout the pinch
+     * gesture to run animations that interpolate throughout the gesture.
+     * @param interpolatedProgress The progress from 0 to 1, where 0 is overview and 1 is workspace.
+     */
+    public void setAnimationProgress(float interpolatedProgress) {
+        float interpolatedScale = interpolatedProgress * (1f - mOverviewScale) + mOverviewScale;
+        float interpolatedTranslationY = (1f - interpolatedProgress) * mOverviewTranslationY;
+        mWorkspace.setScaleX(interpolatedScale);
+        mWorkspace.setScaleY(interpolatedScale);
+        mWorkspace.setTranslationY(interpolatedTranslationY);
+        setOverviewPanelsAlpha(1f - interpolatedProgress, 0);
+    }
+
+    /**
+     * Animates certain properties based on which threshold was passed, and in what direction. The
+     * starting state must also be taken into account because the thresholds mean different things
+     * when going from workspace to overview and vice versa.
+     * @param threshold One of {@link PinchThresholdManager#THRESHOLD_ONE},
+     *                  {@link PinchThresholdManager#THRESHOLD_TWO}, or
+     *                  {@link PinchThresholdManager#THRESHOLD_THREE}
+     * @param startState {@link Workspace.State#NORMAL} or {@link Workspace.State#OVERVIEW}.
+     * @param goingTowards {@link Workspace.State#NORMAL} or {@link Workspace.State#OVERVIEW}.
+     *                     Note that this doesn't have to be the opposite of startState;
+     */
+    public void animateThreshold(float threshold, Workspace.State startState,
+            Workspace.State goingTowards) {
+        if (threshold == PinchThresholdManager.THRESHOLD_ONE) {
+            if (startState == OVERVIEW) {
+                animateOverviewPanelButtons(goingTowards == OVERVIEW);
+            } else if (startState == NORMAL) {
+                animateHotseatAndQsb(goingTowards == NORMAL);
+            }
+        } else if (threshold == PinchThresholdManager.THRESHOLD_TWO) {
+            if (startState == OVERVIEW) {
+                animateHotseatAndQsb(goingTowards == NORMAL);
+                animateScrim(goingTowards == OVERVIEW);
+            } else if (startState == NORMAL) {
+                animateOverviewPanelButtons(goingTowards == OVERVIEW);
+                animateScrim(goingTowards == OVERVIEW);
+            }
+        } else if (threshold == PinchThresholdManager.THRESHOLD_THREE) {
+            // Passing threshold 3 ends the pinch and snaps to the new state.
+            if (startState == OVERVIEW && goingTowards == NORMAL) {
+                mLauncher.showWorkspace(true);
+                mWorkspace.snapToPage(mWorkspace.getCurrentPage());
+            } else if (startState == NORMAL && goingTowards == OVERVIEW) {
+                mLauncher.showOverviewMode(true);
+            }
+        } else {
+            Log.e(TAG, "Received unknown threshold to animate: " + threshold);
+        }
+    }
+
+    private void setOverviewPanelsAlpha(float alpha, int duration) {
+        mWorkspace.getVisiblePages(mVisiblePageRange);
+        for (int i = mVisiblePageRange[0]; i <= mVisiblePageRange[1]; i++) {
+            View page = mWorkspace.getPageAt(i);
+            if (!mWorkspace.shouldDrawChild(page)) {
+                continue;
+            }
+            if (duration == 0) {
+                ((CellLayout) page).setBackgroundAlpha(alpha);
+            } else {
+                ObjectAnimator.ofFloat(page, "backgroundAlpha", alpha)
+                        .setDuration(duration).start();
+            }
+        }
+    }
+
+    private void animateHotseatAndQsb(boolean show) {
+        startAnimator(INDEX_HOTSEAT,
+                mWorkspace.createHotseatAlphaAnimator(show ? 1 : 0), THRESHOLD_ANIM_DURATION);
+        startAnimator(INDEX_QSB, mWorkspace.mQsbAlphaController.animateAlphaAtIndex(
+                show ? 1 : 0, Workspace.QSB_ALPHA_INDEX_STATE_CHANGE), THRESHOLD_ANIM_DURATION);
+    }
+
+    private void animateOverviewPanelButtons(boolean show) {
+        animateShowHideView(INDEX_OVERVIEW_PANEL_BUTTONS, mLauncher.getOverviewPanel(), show);
+    }
+
+    private void animateScrim(boolean show) {
+        float endValue = show ? mWorkspace.getStateTransitionAnimation().mWorkspaceScrimAlpha : 0;
+        startAnimator(INDEX_SCRIM,
+                ObjectAnimator.ofFloat(mLauncher.getDragLayer(), "backgroundAlpha", endValue),
+                mNormalOverviewTransitionDuration);
+    }
+
+    private void animateShowHideView(int index, final View view, boolean show) {
+        Animator animator = new LauncherViewPropertyAnimator(view).alpha(show ? 1 : 0).withLayer();
+        if (show) {
+            view.setVisibility(View.VISIBLE);
+        } else {
+            animator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    view.setVisibility(View.INVISIBLE);
+                }
+            });
+        }
+        startAnimator(index, animator, THRESHOLD_ANIM_DURATION);
+    }
+
+    private void startAnimator(int index, Animator animator, long duration) {
+        if (mAnimators[index] != null) {
+            mAnimators[index].removeAllListeners();
+            mAnimators[index].cancel();
+        }
+        mAnimators[index] = animator;
+        mAnimators[index].setInterpolator(INTERPOLATOR);
+        mAnimators[index].setDuration(duration).start();
+    }
+}
diff --git a/src/com/android/launcher3/PinchThresholdManager.java b/src/com/android/launcher3/PinchThresholdManager.java
new file mode 100644
index 0000000..52aac17
--- /dev/null
+++ b/src/com/android/launcher3/PinchThresholdManager.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+/**
+ * Keeps track of when thresholds are passed during a pinch gesture,
+ * used to inform {@link PinchAnimationManager} throughout.
+ *
+ * @see PinchToOverviewListener
+ * @see PinchAnimationManager
+ */
+public class PinchThresholdManager {
+    public static final float THRESHOLD_ZERO = 0.0f;
+    public static final float THRESHOLD_ONE = 0.40f;
+    public static final float THRESHOLD_TWO = 0.70f;
+    public static final float THRESHOLD_THREE = 0.95f;
+
+    private Workspace mWorkspace;
+
+    private float mPassedThreshold = THRESHOLD_ZERO;
+
+    public PinchThresholdManager(Workspace workspace) {
+        mWorkspace = workspace;
+    }
+
+    /**
+     * Uses the pinch progress to determine whether a threshold has been passed,
+     * and asks the {@param animationManager} to animate if so.
+     * @param progress From 0 to 1, where 0 is overview and 1 is workspace.
+     * @param animationManager Animates the threshold change if one is passed.
+     * @return The last passed threshold, one of
+     *         {@link PinchThresholdManager#THRESHOLD_ZERO},
+     *         {@link PinchThresholdManager#THRESHOLD_ONE},
+     *         {@link PinchThresholdManager#THRESHOLD_TWO}, or
+     *         {@link PinchThresholdManager#THRESHOLD_THREE}
+     */
+    public float updateAndAnimatePassedThreshold(float progress,
+            PinchAnimationManager animationManager) {
+        if (!mWorkspace.isInOverviewMode()) {
+            // Invert the progress, because going from workspace to overview is 1 to 0.
+            progress = 1f - progress;
+        }
+
+        float previousPassedThreshold = mPassedThreshold;
+
+        if (progress < THRESHOLD_ONE) {
+            mPassedThreshold = THRESHOLD_ZERO;
+        } else if (progress < THRESHOLD_TWO) {
+            mPassedThreshold = THRESHOLD_ONE;
+        } else if (progress < THRESHOLD_THREE) {
+            mPassedThreshold = THRESHOLD_TWO;
+        } else {
+            mPassedThreshold = THRESHOLD_THREE;
+        }
+
+        if (mPassedThreshold != previousPassedThreshold) {
+            Workspace.State fromState = mWorkspace.isInOverviewMode() ? Workspace.State.OVERVIEW
+                    : Workspace.State.NORMAL;
+            Workspace.State toState = mWorkspace.isInOverviewMode() ? Workspace.State.NORMAL
+                    : Workspace.State.OVERVIEW;
+            float thresholdToAnimate = mPassedThreshold;
+            if (mPassedThreshold < previousPassedThreshold) {
+                // User reversed pinch, so heading back to the state that they started from.
+                toState = fromState;
+                thresholdToAnimate = previousPassedThreshold;
+            }
+            animationManager.animateThreshold(thresholdToAnimate, fromState, toState);
+        }
+        return mPassedThreshold;
+    }
+
+    public float getPassedThreshold() {
+        return mPassedThreshold;
+    }
+
+    public void reset() {
+        mPassedThreshold = THRESHOLD_ZERO;
+    }
+}
diff --git a/src/com/android/launcher3/PinchToOverviewListener.java b/src/com/android/launcher3/PinchToOverviewListener.java
new file mode 100644
index 0000000..48a75d1
--- /dev/null
+++ b/src/com/android/launcher3/PinchToOverviewListener.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+
+import com.android.launcher3.util.TouchController;
+
+/**
+ * Detects pinches and animates the Workspace to/from overview mode.
+ *
+ * Usage: Pass MotionEvents to onInterceptTouchEvent() and onTouchEvent(). This class will handle
+ * the pinch detection, and use {@link PinchAnimationManager} to handle the animations.
+ *
+ * @see PinchThresholdManager
+ * @see PinchAnimationManager
+ */
+public class PinchToOverviewListener extends ScaleGestureDetector.SimpleOnScaleGestureListener
+        implements TouchController {
+    private static final float OVERVIEW_PROGRESS = 0f;
+    private static final float WORKSPACE_PROGRESS = 1f;
+    /**
+     * The velocity threshold at which a pinch will be completed instead of canceled,
+     * even if the first threshold has not been passed. Measured in progress / millisecond
+     */
+    private static final float FLING_VELOCITY = 0.003f;
+
+    private ScaleGestureDetector mPinchDetector;
+    private Launcher mLauncher;
+    private Workspace mWorkspace = null;
+    private boolean mPinchStarted = false;
+    private float mPreviousProgress;
+    private float mProgressDelta;
+    private long mPreviousTimeMillis;
+    private long mTimeDelta;
+    private boolean mPinchCanceled = false;
+    private TimeInterpolator mInterpolator;
+
+    private PinchThresholdManager mThresholdManager;
+    private PinchAnimationManager mAnimationManager;
+
+    public PinchToOverviewListener(Launcher launcher) {
+        mLauncher = launcher;
+        mPinchDetector = new ScaleGestureDetector((Context) mLauncher, this);
+    }
+
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        mPinchDetector.onTouchEvent(ev);
+        return mPinchStarted;
+    }
+
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (mPinchStarted) {
+            if (ev.getPointerCount() > 2) {
+                // Using more than two fingers causes weird behavior, so just cancel the pinch.
+                cancelPinch(mPreviousProgress, -1);
+            } else {
+                return mPinchDetector.onTouchEvent(ev);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onScaleBegin(ScaleGestureDetector detector) {
+        if (mLauncher.mState != Launcher.State.WORKSPACE || mLauncher.isOnCustomContent()) {
+            // Don't listen for the pinch gesture if on all apps, widget picker, -1, etc.
+            return false;
+        }
+        if (mAnimationManager != null && mAnimationManager.isAnimating()) {
+            // Don't listen for the pinch gesture if we are already animating from a previous one.
+            return false;
+        }
+        if (mLauncher.isWorkspaceLocked()) {
+            // Don't listen for the pinch gesture if the workspace isn't ready.
+            return false;
+        }
+        if (mWorkspace == null) {
+            mWorkspace = mLauncher.getWorkspace();
+            mThresholdManager = new PinchThresholdManager(mWorkspace);
+            mAnimationManager = new PinchAnimationManager(mLauncher);
+        }
+        if (mWorkspace.isSwitchingState() || mWorkspace.mScrollInteractionBegan) {
+            // Don't listen for the pinch gesture while switching state, as it will cause a jump
+            // once the state switching animation is complete.
+            return false;
+        }
+        if (mLauncher.getTopFloatingView() != null) {
+            // Don't listen for the pinch gesture if a floating view is open.
+            return false;
+        }
+
+        mPreviousProgress = mWorkspace.isInOverviewMode() ? OVERVIEW_PROGRESS : WORKSPACE_PROGRESS;
+        mPreviousTimeMillis = System.currentTimeMillis();
+        mInterpolator = mWorkspace.isInOverviewMode() ? new LogDecelerateInterpolator(100, 0)
+                : new LogAccelerateInterpolator(100, 0);
+        mPinchStarted = true;
+        mWorkspace.onLauncherTransitionPrepare(mLauncher, false, true);
+        return true;
+    }
+
+    @Override
+    public void onScaleEnd(ScaleGestureDetector detector) {
+        super.onScaleEnd(detector);
+
+        float progressVelocity = mProgressDelta / mTimeDelta;
+        float passedThreshold = mThresholdManager.getPassedThreshold();
+        boolean isFling = mWorkspace.isInOverviewMode() && progressVelocity >= FLING_VELOCITY
+                || !mWorkspace.isInOverviewMode() && progressVelocity <= -FLING_VELOCITY;
+        boolean shouldCancelPinch = !isFling && passedThreshold < PinchThresholdManager.THRESHOLD_ONE;
+        // If we are going towards overview, mPreviousProgress is how much further we need to
+        // go, since it is going from 1 to 0. If we are going to workspace, we want
+        // 1 - mPreviousProgress.
+        float remainingProgress = mPreviousProgress;
+        if (mWorkspace.isInOverviewMode() || shouldCancelPinch) {
+            remainingProgress = 1f - mPreviousProgress;
+        }
+        int duration = computeDuration(remainingProgress, progressVelocity);
+        if (shouldCancelPinch) {
+            cancelPinch(mPreviousProgress, duration);
+        } else if (passedThreshold < PinchThresholdManager.THRESHOLD_THREE) {
+            float toProgress = mWorkspace.isInOverviewMode() ?
+                    WORKSPACE_PROGRESS : OVERVIEW_PROGRESS;
+            mAnimationManager.animateToProgress(mPreviousProgress, toProgress, duration,
+                    mThresholdManager);
+        } else {
+            mThresholdManager.reset();
+            mWorkspace.onLauncherTransitionEnd(mLauncher, false, true);
+        }
+        mPinchStarted = false;
+        mPinchCanceled = false;
+    }
+
+    /**
+     * Compute the amount of time required to complete the transition based on the current pinch
+     * speed. If this time is too long, instead return the normal duration, ignoring the speed.
+     */
+    private int computeDuration(float remainingProgress, float progressVelocity) {
+        float progressSpeed = Math.abs(progressVelocity);
+        int remainingMillis = (int) (remainingProgress / progressSpeed);
+        return Math.min(remainingMillis, mAnimationManager.getNormalOverviewTransitionDuration());
+    }
+
+    /**
+     * Cancels the current pinch, returning back to where the pinch started (either workspace or
+     * overview). If duration is -1, the default overview transition duration is used.
+     */
+    private void cancelPinch(float currentProgress, int duration) {
+        if (mPinchCanceled) return;
+        mPinchCanceled = true;
+        float toProgress = mWorkspace.isInOverviewMode() ? OVERVIEW_PROGRESS : WORKSPACE_PROGRESS;
+        mAnimationManager.animateToProgress(currentProgress, toProgress, duration,
+                mThresholdManager);
+        mPinchStarted = false;
+    }
+
+    @Override
+    public boolean onScale(ScaleGestureDetector detector) {
+        if (mThresholdManager.getPassedThreshold() == PinchThresholdManager.THRESHOLD_THREE) {
+            // We completed the pinch, so stop listening to further movement until user lets go.
+            return true;
+        }
+        if (mLauncher.getDragController().isDragging()) {
+            mLauncher.getDragController().cancelDrag();
+        }
+
+        float pinchDist = detector.getCurrentSpan() - detector.getPreviousSpan();
+        if (pinchDist < 0 && mWorkspace.isInOverviewMode() ||
+                pinchDist > 0 && !mWorkspace.isInOverviewMode()) {
+            // Pinching the wrong way, so ignore.
+            return false;
+        }
+        // Pinch distance must equal the workspace width before switching states.
+        int pinchDistanceToCompleteTransition = mWorkspace.getWidth();
+        float overviewScale = mWorkspace.getOverviewModeShrinkFactor();
+        float initialWorkspaceScale = mWorkspace.isInOverviewMode() ? overviewScale : 1f;
+        float pinchScale = initialWorkspaceScale + pinchDist / pinchDistanceToCompleteTransition;
+        // Bound the scale between the overview scale and the normal workspace scale (1f).
+        pinchScale = Math.max(overviewScale, Math.min(pinchScale, 1f));
+        // Progress ranges from 0 to 1, where 0 corresponds to the overview scale and 1
+        // corresponds to the normal workspace scale (1f).
+        float progress = (pinchScale - overviewScale) / (1f - overviewScale);
+        float interpolatedProgress = mInterpolator.getInterpolation(progress);
+
+        mAnimationManager.setAnimationProgress(interpolatedProgress);
+        float passedThreshold = mThresholdManager.updateAndAnimatePassedThreshold(
+                interpolatedProgress, mAnimationManager);
+        if (passedThreshold == PinchThresholdManager.THRESHOLD_THREE) {
+            return true;
+        }
+
+        mProgressDelta = interpolatedProgress - mPreviousProgress;
+        mPreviousProgress = interpolatedProgress;
+        mTimeDelta = System.currentTimeMillis() - mPreviousTimeMillis;
+        mPreviousTimeMillis = System.currentTimeMillis();
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java
index 908c8b9..b064c47 100644
--- a/src/com/android/launcher3/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/PreloadIconDrawable.java
@@ -12,7 +12,7 @@
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 
-class PreloadIconDrawable extends Drawable {
+public class PreloadIconDrawable extends Drawable {
 
     private static final float ANIMATION_PROGRESS_STOPPED = -1.0f;
     private static final float ANIMATION_PROGRESS_STARTED = 0f;
@@ -30,7 +30,7 @@
     private boolean mIndicatorRectDirty;
 
     private final Paint mPaint;
-    final Drawable mIcon;
+    public final Drawable mIcon;
 
     private Drawable mBgDrawable;
     private int mRingOutset;
diff --git a/src/com/android/launcher3/QsbBlockerView.java b/src/com/android/launcher3/QsbBlockerView.java
new file mode 100644
index 0000000..6a2bce0
--- /dev/null
+++ b/src/com/android/launcher3/QsbBlockerView.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.launcher3.Workspace.State;
+
+/**
+ * A simple view used to show the region blocked by QSB during drag and drop.
+ */
+public class QsbBlockerView extends View implements Workspace.OnStateChangeListener {
+
+    private static final int VISIBLE_ALPHA = 100;
+
+    private final Paint mBgPaint;
+
+    public QsbBlockerView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        mBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mBgPaint.setColor(Color.WHITE);
+        mBgPaint.setAlpha(0);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        Workspace w = Launcher.getLauncher(getContext()).getWorkspace();
+        w.setOnStateChangeListener(this);
+        prepareStateChange(w.getState(), null);
+    }
+
+    @Override
+    public void prepareStateChange(State toState, AnimatorSet targetAnim) {
+        int finalAlpha = getAlphaForState(toState);
+        if (targetAnim == null) {
+            mBgPaint.setAlpha(finalAlpha);
+            invalidate();
+        } else {
+            ObjectAnimator anim = ObjectAnimator.ofArgb(mBgPaint, "alpha", finalAlpha);
+            anim.addUpdateListener(new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                    invalidate();
+                }
+            });
+            targetAnim.play(anim);
+        }
+    }
+
+    private static int getAlphaForState(State state) {
+        switch (state) {
+            case SPRING_LOADED:
+            case OVERVIEW:
+            case OVERVIEW_HIDDEN:
+                return VISIBLE_ALPHA;
+        }
+        return 0;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        canvas.drawPaint(mBgPaint);
+    }
+}
diff --git a/src/com/android/launcher3/QsbContainerView.java b/src/com/android/launcher3/QsbContainerView.java
new file mode 100644
index 0000000..02d8a13
--- /dev/null
+++ b/src/com/android/launcher3/QsbContainerView.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.SearchManager;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+
+/**
+ * A frame layout which contains a QSB. This internally uses fragment to bind the view, which
+ * allows it to contain the logic for {@link Fragment#startActivityForResult(Intent, int)}.
+ */
+public class QsbContainerView extends FrameLayout {
+
+    public QsbContainerView(Context context) {
+        super(context);
+    }
+
+    public QsbContainerView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public QsbContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    public void setPadding(int left, int top, int right, int bottom) {
+        super.setPadding(0, 0, 0, 0);
+    }
+
+    /**
+     * A fragment to display the QSB.
+     */
+    public static class QsbFragment extends Fragment implements View.OnClickListener {
+
+        private static final int REQUEST_BIND_QSB = 1;
+        private static final String QSB_WIDGET_ID = "qsb_widget_id";
+
+        private static int sSavedWidgetId = -1;
+
+        private AppWidgetProviderInfo mWidgetInfo;
+        private LauncherAppWidgetHostView mQsb;
+
+        private BroadcastReceiver mRebindReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                rebindFragment();
+            }
+        };
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+
+            IntentFilter filter = new IntentFilter(Launcher.ACTION_APPWIDGET_HOST_RESET);
+            filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
+            getActivity().registerReceiver(mRebindReceiver, filter);
+        }
+
+        private FrameLayout mWrapper;
+
+        @Override
+        public View onCreateView(
+                LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+
+            if (savedInstanceState != null) {
+                sSavedWidgetId = savedInstanceState.getInt(QSB_WIDGET_ID, -1);
+            }
+            mWrapper = new FrameLayout(getActivity());
+            mWrapper.addView(createQsb(inflater, mWrapper));
+            return mWrapper;
+        }
+
+        private View createQsb(LayoutInflater inflater, ViewGroup container) {
+            Launcher launcher = Launcher.getLauncher(getActivity());
+            mWidgetInfo = getSearchWidgetProvider(launcher);
+            if (mWidgetInfo == null) {
+                // There is no search provider, just show the default widget.
+                return getDefaultView(inflater, container, false);
+            }
+
+            SharedPreferences prefs = Utilities.getPrefs(launcher);
+            AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(launcher);
+            LauncherAppWidgetHost widgetHost = launcher.getAppWidgetHost();
+            InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
+
+            Bundle opts = new Bundle();
+            Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(launcher, idp.numColumns, 1, null);
+            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left);
+            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top);
+            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right);
+            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom);
+
+            int widgetId = prefs.getInt(QSB_WIDGET_ID, -1);
+            AppWidgetProviderInfo widgetInfo = widgetManager.getAppWidgetInfo(widgetId);
+            boolean isWidgetBound = (widgetInfo != null) &&
+                    widgetInfo.provider.equals(mWidgetInfo.provider);
+
+            if (!isWidgetBound) {
+                // widgetId is already bound and its not the correct provider.
+                // Delete the widget id.
+                if (widgetId > -1) {
+                    widgetHost.deleteAppWidgetId(widgetId);
+                    widgetId = -1;
+                }
+
+                widgetId = widgetHost.allocateAppWidgetId();
+                isWidgetBound = widgetManager.bindAppWidgetIdIfAllowed(widgetId, mWidgetInfo, opts);
+                if (!isWidgetBound) {
+                    widgetHost.deleteAppWidgetId(widgetId);
+                    widgetId = -1;
+                }
+            }
+
+            if (isWidgetBound) {
+                mQsb = (LauncherAppWidgetHostView)
+                        widgetHost.createView(launcher, widgetId, mWidgetInfo);
+                mQsb.setId(R.id.qsb_widget);
+                mQsb.mErrorViewId = R.layout.qsb_default_view;
+
+                if (!Utilities.containsAll(AppWidgetManager.getInstance(launcher)
+                        .getAppWidgetOptions(widgetId), opts)) {
+                    mQsb.updateAppWidgetOptions(opts);
+                }
+                mQsb.setPadding(0, 0, 0, 0);
+                return mQsb;
+            }
+
+            // Return a default widget with setup icon.
+            return getDefaultView(inflater, container, true);
+        }
+
+        @Override
+        public void onClick(View view) {
+            if (view.getId() == R.id.btn_qsb_search) {
+                getActivity().startSearch("", false, null, true);
+            } else if (view.getId() == R.id.btn_qsb_setup) {
+                // Allocate a new widget id for QSB
+                sSavedWidgetId = Launcher.getLauncher(getActivity())
+                        .getAppWidgetHost().allocateAppWidgetId();
+                // Start intent for bind the widget
+                Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
+                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, sSavedWidgetId);
+                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, mWidgetInfo.provider);
+                startActivityForResult(intent, REQUEST_BIND_QSB);
+            }
+        }
+
+        @Override
+        public void onSaveInstanceState(Bundle outState) {
+            super.onSaveInstanceState(outState);
+            outState.putInt(QSB_WIDGET_ID, sSavedWidgetId);
+        }
+
+        @Override
+        public void onActivityResult(int requestCode, int resultCode, Intent data) {
+            if (requestCode == REQUEST_BIND_QSB) {
+                if (resultCode == Activity.RESULT_OK) {
+                    int widgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
+                            sSavedWidgetId);
+                    Utilities.getPrefs(getActivity()).edit().putInt(QSB_WIDGET_ID, widgetId).apply();
+                    sSavedWidgetId = -1;
+                    rebindFragment();
+                } else if (sSavedWidgetId != -1) {
+                    Launcher.getLauncher(getActivity()).getAppWidgetHost()
+                            .deleteAppWidgetId(sSavedWidgetId);
+                    sSavedWidgetId = -1;
+                }
+            }
+        }
+
+        @Override
+        public void onResume() {
+            super.onResume();
+            if (mQsb != null && mQsb.isReinflateRequired()) {
+                rebindFragment();
+            }
+        }
+
+        @Override
+        public void onDestroy() {
+            getActivity().unregisterReceiver(mRebindReceiver);
+            super.onDestroy();
+        }
+
+        private void rebindFragment() {
+            if (mWrapper != null && getActivity() != null) {
+                mWrapper.removeAllViews();
+                mWrapper.addView(createQsb(getActivity().getLayoutInflater(), mWrapper));
+            }
+        }
+
+        private View getDefaultView(LayoutInflater inflater, ViewGroup parent, boolean showSetup) {
+            View v = inflater.inflate(R.layout.qsb_default_view, parent, false);
+            if (showSetup) {
+                View setupButton = v.findViewById(R.id.btn_qsb_setup);
+                setupButton.setVisibility(View.VISIBLE);
+                setupButton.setOnClickListener(this);
+            }
+            v.findViewById(R.id.btn_qsb_search).setOnClickListener(this);
+            return v;
+        }
+    }
+
+    /**
+     * Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}
+     * provided by the same package which is set to be global search activity.
+     * If widgetCategory is not supported, or no such widget is found, returns the first widget
+     * provided by the package.
+     */
+    public static AppWidgetProviderInfo getSearchWidgetProvider(Context context) {
+        SearchManager searchManager =
+                (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
+        ComponentName searchComponent = searchManager.getGlobalSearchActivity();
+        if (searchComponent == null) return null;
+        String providerPkg = searchComponent.getPackageName();
+
+        AppWidgetProviderInfo defaultWidgetForSearchPackage = null;
+
+        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
+        for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
+            if (info.provider.getPackageName().equals(providerPkg) && info.configure == null) {
+                if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
+                    return info;
+                } else if (defaultWidgetForSearchPackage == null) {
+                    defaultWidgetForSearchPackage = info;
+                }
+            }
+        }
+        return defaultWidgetForSearchPackage;
+    }
+}
diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java
deleted file mode 100644
index f7288a0..0000000
--- a/src/com/android/launcher3/SearchDropTargetBar.java
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateDecelerateInterpolator;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.FrameLayout;
-
-import com.android.launcher3.util.Thunk;
-
-/*
- * Ths bar will manage the transition between the QSB search bar and the delete drop
- * targets so that each of the individual IconDropTargets don't have to.
- */
-public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener {
-
-    private static final TimeInterpolator MOVE_DOWN_INTERPOLATOR = new DecelerateInterpolator(0.6f);
-    private static final TimeInterpolator MOVE_UP_INTERPOLATOR = new DecelerateInterpolator(1.5f);
-    private static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator();
-
-    /** The different states that the search bar space can be in. */
-    public enum State {
-        INVISIBLE             (0f, 0f, 0f),
-        INVISIBLE_TRANSLATED  (0f, 0f, -1f),
-        SEARCH_BAR            (1f, 0f, 0f),
-        DROP_TARGET           (0f, 1f, 0f);
-
-        private final float mSearchBarAlpha;
-        private final float mDropTargetBarAlpha;
-        private final float mTranslation;
-
-        State(float sbAlpha, float dtbAlpha, float translation) {
-            mSearchBarAlpha = sbAlpha;
-            mDropTargetBarAlpha = dtbAlpha;
-            mTranslation = translation;
-        }
-
-    }
-
-    private static int DEFAULT_DRAG_FADE_DURATION = 175;
-
-    private AnimatorSet mCurrentAnimation;
-
-    private State mState = State.SEARCH_BAR;
-    @Thunk View mQSB;
-    @Thunk View mDropTargetBar;
-    private boolean mDeferOnDragEnd = false;
-    @Thunk boolean mAccessibilityEnabled = false;
-
-    // Drop targets
-    private ButtonDropTarget mInfoDropTarget;
-    private ButtonDropTarget mDeleteDropTarget;
-    private ButtonDropTarget mUninstallDropTarget;
-
-    public SearchDropTargetBar(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public SearchDropTargetBar(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    public void setup(Launcher launcher, DragController dragController) {
-        dragController.addDragListener(this);
-        dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
-
-        dragController.addDragListener(mInfoDropTarget);
-        dragController.addDragListener(mDeleteDropTarget);
-        dragController.addDragListener(mUninstallDropTarget);
-
-        dragController.addDropTarget(mInfoDropTarget);
-        dragController.addDropTarget(mDeleteDropTarget);
-        dragController.addDropTarget(mUninstallDropTarget);
-
-        mInfoDropTarget.setLauncher(launcher);
-        mDeleteDropTarget.setLauncher(launcher);
-        mUninstallDropTarget.setLauncher(launcher);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        // Get the individual components
-        mDropTargetBar = findViewById(R.id.drag_target_bar);
-        mInfoDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.info_target_text);
-        mDeleteDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.delete_target_text);
-        mUninstallDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.uninstall_target_text);
-
-        mInfoDropTarget.setSearchDropTargetBar(this);
-        mDeleteDropTarget.setSearchDropTargetBar(this);
-        mUninstallDropTarget.setSearchDropTargetBar(this);
-
-        // Create the various fade animations
-        mDropTargetBar.setAlpha(0f);
-        AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
-    }
-
-    public void setQsbSearchBar(View qsb) {
-        mQSB = qsb;
-    }
-
-    /**
-     * Animates the current search bar state to a new state.  If the {@param duration} is 0, then
-     * the state is applied immediately.
-     */
-    public void animateToState(State newState, int duration) {
-        animateToState(newState, duration, null);
-    }
-
-    public void animateToState(State newState, int duration, AnimatorSet animation) {
-        if (mState != newState) {
-            mState = newState;
-
-            // Update the accessibility state
-            AccessibilityManager am = (AccessibilityManager)
-                    getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-            mAccessibilityEnabled = am.isEnabled();
-
-            if (mCurrentAnimation != null) {
-                mCurrentAnimation.cancel();
-                mCurrentAnimation = null;
-            }
-            mCurrentAnimation = null;
-
-            if (duration > 0) {
-                mCurrentAnimation = new AnimatorSet();
-                mCurrentAnimation.setDuration(duration);
-
-                animateAlpha(mDropTargetBar, mState.mDropTargetBarAlpha, DEFAULT_INTERPOLATOR);
-            } else {
-                mDropTargetBar.setAlpha(mState.mDropTargetBarAlpha);
-                AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
-            }
-
-            if (mQSB != null) {
-                boolean isVertical = ((Launcher) getContext()).getDeviceProfile()
-                        .isVerticalBarLayout();
-                float translation = isVertical ? 0 : mState.mTranslation * getMeasuredHeight();
-
-                if (duration > 0) {
-                    int translationChange = Float.compare(mQSB.getTranslationY(), translation);
-
-                    animateAlpha(mQSB, mState.mSearchBarAlpha,
-                            translationChange == 0 ? DEFAULT_INTERPOLATOR
-                                    : (translationChange < 0 ? MOVE_DOWN_INTERPOLATOR
-                                    : MOVE_UP_INTERPOLATOR));
-
-                    if (translationChange != 0) {
-                        mCurrentAnimation.play(
-                                ObjectAnimator.ofFloat(mQSB, View.TRANSLATION_Y, translation));
-                    }
-                } else {
-                    mQSB.setTranslationY(translation);
-                    mQSB.setAlpha(mState.mSearchBarAlpha);
-                    AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled);
-                }
-            }
-
-            // Start the final animation
-            if (duration > 0) {
-                if (animation != null) {
-                    animation.play(mCurrentAnimation);
-                } else {
-                    mCurrentAnimation.start();
-                }
-            }
-        }
-    }
-
-    private void animateAlpha(View v, float alpha, TimeInterpolator interpolator) {
-        if (Float.compare(v.getAlpha(), alpha) != 0) {
-            ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.ALPHA, alpha);
-            anim.setInterpolator(interpolator);
-            anim.addListener(new ViewVisiblilyUpdateHandler(v));
-            mCurrentAnimation.play(anim);
-        }
-    }
-
-    /*
-     * DragController.DragListener implementation
-     */
-    @Override
-    public void onDragStart(DragSource source, Object info, int dragAction) {
-        animateToState(State.DROP_TARGET, DEFAULT_DRAG_FADE_DURATION);
-    }
-
-    /**
-     * This is called to defer hiding the delete drop target until the drop animation has completed,
-     * instead of hiding immediately when the drag has ended.
-     */
-    public void deferOnDragEnd() {
-        mDeferOnDragEnd = true;
-    }
-
-    @Override
-    public void onDragEnd() {
-        if (!mDeferOnDragEnd) {
-            animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION);
-        } else {
-            mDeferOnDragEnd = false;
-        }
-    }
-
-    /**
-     * @return the bounds of the QSB search bar.
-     */
-    public Rect getSearchBarBounds() {
-        if (mQSB != null) {
-            final int[] pos = new int[2];
-            mQSB.getLocationOnScreen(pos);
-
-            final Rect rect = new Rect();
-            rect.left = pos[0];
-            rect.top = pos[1];
-            rect.right = pos[0] + mQSB.getWidth();
-            rect.bottom = pos[1] + mQSB.getHeight();
-            return rect;
-        } else {
-            return null;
-        }
-    }
-
-    public void enableAccessibleDrag(boolean enable) {
-        if (mQSB != null) {
-            mQSB.setVisibility(enable ? View.GONE : View.VISIBLE);
-        }
-        mInfoDropTarget.enableAccessibleDrag(enable);
-        mDeleteDropTarget.enableAccessibleDrag(enable);
-        mUninstallDropTarget.enableAccessibleDrag(enable);
-    }
-
-    private class ViewVisiblilyUpdateHandler extends AnimatorListenerAdapter {
-        private final View mView;
-
-        ViewVisiblilyUpdateHandler(View v) {
-            mView = v;
-        }
-
-        @Override
-        public void onAnimationStart(Animator animation) {
-            // Ensure that the view is visible for the animation
-            mView.setVisibility(View.VISIBLE);
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation){
-            AlphaUpdateListener.updateVisibility(mView, mAccessibilityEnabled);
-        }
-
-    }
-}
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index dab71c8..cedeb39 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -17,11 +17,14 @@
 package com.android.launcher3;
 
 import android.app.Activity;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
 import android.os.Bundle;
+import android.os.Handler;
 import android.preference.Preference;
-import android.preference.Preference.OnPreferenceChangeListener;
 import android.preference.PreferenceFragment;
-import android.preference.SwitchPreference;
+import android.provider.Settings;
+import android.provider.Settings.System;
 
 /**
  * Settings activity for Launcher. Currently implements the following setting: Allow rotation
@@ -40,37 +43,70 @@
     /**
      * This fragment shows the launcher preferences.
      */
-    public static class LauncherSettingsFragment extends PreferenceFragment
-            implements OnPreferenceChangeListener {
+    public static class LauncherSettingsFragment extends PreferenceFragment {
+
+        private SystemDisplayRotationLockObserver mRotationLockObserver;
+
         @Override
         public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
+            getPreferenceManager().setSharedPreferencesName(LauncherFiles.SHARED_PREFERENCES_KEY);
             addPreferencesFromResource(R.xml.launcher_preferences);
 
-            SwitchPreference pref = (SwitchPreference) findPreference(
-                    Utilities.ALLOW_ROTATION_PREFERENCE_KEY);
-            pref.setPersistent(false);
+            // Setup allow rotation preference
+            Preference rotationPref = findPreference(Utilities.ALLOW_ROTATION_PREFERENCE_KEY);
+            if (getResources().getBoolean(R.bool.allow_rotation)) {
+                // Launcher supports rotation by default. No need to show this setting.
+                getPreferenceScreen().removePreference(rotationPref);
+            } else {
+                ContentResolver resolver = getActivity().getContentResolver();
+                mRotationLockObserver = new SystemDisplayRotationLockObserver(rotationPref, resolver);
 
-            Bundle extras = new Bundle();
-            extras.putBoolean(LauncherSettings.Settings.EXTRA_DEFAULT_VALUE, false);
-            Bundle value = getActivity().getContentResolver().call(
-                    LauncherSettings.Settings.CONTENT_URI,
-                    LauncherSettings.Settings.METHOD_GET_BOOLEAN,
-                    Utilities.ALLOW_ROTATION_PREFERENCE_KEY, extras);
-            pref.setChecked(value.getBoolean(LauncherSettings.Settings.EXTRA_VALUE));
+                // Register a content observer to listen for system setting changes while
+                // this UI is active.
+                resolver.registerContentObserver(
+                        Settings.System.getUriFor(System.ACCELEROMETER_ROTATION),
+                        false, mRotationLockObserver);
 
-            pref.setOnPreferenceChangeListener(this);
+                // Initialize the UI once
+                mRotationLockObserver.onChange(true);
+                rotationPref.setDefaultValue(Utilities.getAllowRotationDefaultValue(getActivity()));
+            }
         }
 
         @Override
-        public boolean onPreferenceChange(Preference preference, Object newValue) {
-            Bundle extras = new Bundle();
-            extras.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, (Boolean) newValue);
-            getActivity().getContentResolver().call(
-                    LauncherSettings.Settings.CONTENT_URI,
-                    LauncherSettings.Settings.METHOD_SET_BOOLEAN,
-                    preference.getKey(), extras);
-            return true;
+        public void onDestroy() {
+            if (mRotationLockObserver != null) {
+                getActivity().getContentResolver().unregisterContentObserver(mRotationLockObserver);
+                mRotationLockObserver = null;
+            }
+            super.onDestroy();
+        }
+    }
+
+    /**
+     * Content observer which listens for system auto-rotate setting changes, and enables/disables
+     * the launcher rotation setting accordingly.
+     */
+    private static class SystemDisplayRotationLockObserver extends ContentObserver {
+
+        private final Preference mRotationPref;
+        private final ContentResolver mResolver;
+
+        public SystemDisplayRotationLockObserver(
+                Preference rotationPref, ContentResolver resolver) {
+            super(new Handler());
+            mRotationPref = rotationPref;
+            mResolver = resolver;
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            boolean enabled = Settings.System.getInt(mResolver,
+                    Settings.System.ACCELEROMETER_ROTATION, 1) == 1;
+            mRotationPref.setEnabled(enabled);
+            mRotationPref.setSummary(enabled
+                    ? R.string.allow_rotation_desc : R.string.allow_rotation_blocked_desc);
         }
     }
 }
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 21e72e9..37cbf98 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -18,7 +18,6 @@
 
 import android.app.WallpaperManager;
 import android.content.Context;
-import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.view.View;
@@ -42,7 +41,6 @@
     private int mHeightGap;
 
     private int mCountX;
-    private int mCountY;
 
     private Launcher mLauncher;
 
@@ -50,7 +48,7 @@
 
     public ShortcutAndWidgetContainer(Context context) {
         super(context);
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
         mWallpaperManager = WallpaperManager.getInstance(context);
     }
 
@@ -61,7 +59,6 @@
         mWidthGap = widthGap;
         mHeightGap = heightGap;
         mCountX = countX;
-        mCountY = countY;
     }
 
     public View getChildAt(int x, int y) {
@@ -79,24 +76,6 @@
     }
 
     @Override
-    protected void dispatchDraw(Canvas canvas) {
-        @SuppressWarnings("all") // suppress dead code warning
-        final boolean debug = false;
-        if (debug) {
-            // Debug drawing for hit space
-            Paint p = new Paint();
-            p.setColor(0x6600FF00);
-            for (int i = getChildCount() - 1; i >= 0; i--) {
-                final View child = getChildAt(i);
-                final CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
-
-                canvas.drawRect(lp.x, lp.y, lp.x + lp.width, lp.y + lp.height, p);
-            }
-        }
-        super.dispatchDraw(canvas);
-    }
-
-    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         int count = getChildCount();
 
@@ -150,7 +129,7 @@
             if (child instanceof LauncherAppWidgetHostView) {
                 // Widgets have their own padding, so skip
             } else {
-                // Otherwise, center the icon
+                // Otherwise, center the icon/folder
                 int cHeight = getCellContentHeight();
                 int cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f));
                 int cellPaddingX = (int) (grid.edgeMarginPx / 2f);
@@ -236,8 +215,14 @@
         }
     }
 
-    @Override
     protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
         super.setChildrenDrawnWithCacheEnabled(enabled);
     }
+
+    @Override
+    public void setLayerType(int layerType, Paint paint) {
+        // When clip children is disabled do not use hardware layer,
+        // as hardware layer forces clip children.
+        super.setLayerType(getClipChildren() ? layerType : LAYER_TYPE_NONE, paint);
+    }
 }
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 60e080e..fb93743 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -16,20 +16,22 @@
 
 package com.android.launcher3;
 
+import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
-import android.util.Log;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.text.TextUtils;
 
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
-
-import java.util.ArrayList;
-import java.util.Arrays;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 
 /**
  * Represents a launchable icon on the workspaces and in folders.
@@ -71,14 +73,7 @@
     /**
      * The intent used to start the application.
      */
-    Intent intent;
-
-    /**
-     * Indicates whether the icon comes from an application's resource (if false)
-     * or from a custom Bitmap (if true.)
-     * TODO: remove this flag
-     */
-    public boolean customIcon;
+    public Intent intent;
 
     /**
      * Indicates whether we're using the default fallback icon instead of something from the
@@ -105,22 +100,32 @@
     /**
      * Indicates that the icon is disabled due to safe mode restrictions.
      */
-    public static final int FLAG_DISABLED_SAFEMODE = 1;
+    public static final int FLAG_DISABLED_SAFEMODE = 1 << 0;
 
     /**
      * Indicates that the icon is disabled as the app is not available.
      */
-    public static final int FLAG_DISABLED_NOT_AVAILABLE = 2;
+    public static final int FLAG_DISABLED_NOT_AVAILABLE = 1 << 1;
 
     /**
      * Indicates that the icon is disabled as the app is suspended
      */
-    public static final int FLAG_DISABLED_SUSPENDED = 4;
+    public static final int FLAG_DISABLED_SUSPENDED = 1 << 2;
 
     /**
      * Indicates that the icon is disabled as the user is in quiet mode.
      */
-    public static final int FLAG_DISABLED_QUIET_USER = 8;
+    public static final int FLAG_DISABLED_QUIET_USER = 1 << 3;
+
+    /**
+     * Indicates that the icon is disabled as the publisher has disabled the actual shortcut.
+     */
+    public static final int FLAG_DISABLED_BY_PUBLISHER = 1 << 4;
+
+    /**
+     * Indicates that the icon is disabled as the user partition is currently locked.
+     */
+    public static final int FLAG_DISABLED_LOCKED_USER = 1 << 5;
 
     /**
      * Could be disabled, if the the app is installed but unavailable (eg. in safe mode or when
@@ -128,6 +133,12 @@
      */
     int isDisabled = DEFAULT;
 
+    /**
+     * A message to display when the user tries to start a disabled shortcut.
+     * This is currently only used for deep shortcuts.
+     */
+    CharSequence disabledMessage;
+
     int status;
 
     /**
@@ -147,14 +158,20 @@
      */
     Intent promisedIntent;
 
-    ShortcutInfo() {
+    public ShortcutInfo() {
         itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
     }
 
+    @Override
     public Intent getIntent() {
         return intent;
     }
 
+    /** Returns {@link #promisedIntent}, or {@link #intent} if promisedIntent is null. */
+    public Intent getPromisedIntent() {
+        return promisedIntent != null ? promisedIntent : intent;
+    }
+
     ShortcutInfo(Intent intent, CharSequence title, CharSequence contentDescription,
             Bitmap icon, UserHandleCompat user) {
         this();
@@ -165,20 +182,17 @@
         this.user = user;
     }
 
-    public ShortcutInfo(Context context, ShortcutInfo info) {
+    public ShortcutInfo(ShortcutInfo info) {
         super(info);
-        title = Utilities.trim(info.title);
+        title = info.title;
         intent = new Intent(info.intent);
-        if (info.iconResource != null) {
-            iconResource = new Intent.ShortcutIconResource();
-            iconResource.packageName = info.iconResource.packageName;
-            iconResource.resourceName = info.iconResource.resourceName;
-        }
+        iconResource = info.iconResource;
         mIcon = info.mIcon; // TODO: should make a copy here.  maybe we don't need this ctor at all
-        customIcon = info.customIcon;
         flags = info.flags;
-        user = info.user;
         status = info.status;
+        mInstallProgress = info.mInstallProgress;
+        isDisabled = info.isDisabled;
+        usingFallbackIcon = info.usingFallbackIcon;
     }
 
     /** TODO: Remove this.  It's only called by ApplicationInfo.makeShortcut. */
@@ -186,11 +200,31 @@
         super(info);
         title = Utilities.trim(info.title);
         intent = new Intent(info.intent);
-        customIcon = false;
         flags = info.flags;
         isDisabled = info.isDisabled;
     }
 
+    public ShortcutInfo(LauncherActivityInfoCompat info, Context context) {
+        user = info.getUser();
+        title = Utilities.trim(info.getLabel());
+        contentDescription = UserManagerCompat.getInstance(context)
+                .getBadgedLabelForUser(info.getLabel(), info.getUser());
+        intent = AppInfo.makeLaunchIntent(context, info, info.getUser());
+        itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+        flags = AppInfo.initFlags(info);
+    }
+
+    /**
+     * Creates a {@link ShortcutInfo} from a {@link ShortcutInfoCompat}.
+     */
+    @TargetApi(Build.VERSION_CODES.N)
+    public ShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
+        user = shortcutInfo.getUserHandle();
+        itemType = LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+        flags = 0;
+        updateFromDeepShortcutInfo(shortcutInfo, context);
+    }
+
     public void setIcon(Bitmap b) {
         mIcon = b;
     }
@@ -225,39 +259,14 @@
         values.put(LauncherSettings.BaseLauncherColumns.INTENT, uri);
         values.put(LauncherSettings.Favorites.RESTORED, status);
 
-        if (customIcon) {
-            values.put(LauncherSettings.BaseLauncherColumns.ICON_TYPE,
-                    LauncherSettings.BaseLauncherColumns.ICON_TYPE_BITMAP);
+        if (!usingFallbackIcon && !usingLowResIcon) {
             writeBitmap(values, mIcon);
-        } else {
-            if (!usingFallbackIcon) {
-                writeBitmap(values, mIcon);
-            }
-            if (iconResource != null) {
-                values.put(LauncherSettings.BaseLauncherColumns.ICON_TYPE,
-                        LauncherSettings.BaseLauncherColumns.ICON_TYPE_RESOURCE);
-                values.put(LauncherSettings.BaseLauncherColumns.ICON_PACKAGE,
-                        iconResource.packageName);
-                values.put(LauncherSettings.BaseLauncherColumns.ICON_RESOURCE,
-                        iconResource.resourceName);
-            }
         }
-    }
-
-    @Override
-    public String toString() {
-        return "ShortcutInfo(title=" + title + "intent=" + intent + "id=" + this.id
-                + " type=" + this.itemType + " container=" + this.container + " screen=" + screenId
-                + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY
-                + " dropPos=" + Arrays.toString(dropPos) + " user=" + user + ")";
-    }
-
-    public static void dumpShortcutInfoList(String tag, String label,
-            ArrayList<ShortcutInfo> list) {
-        Log.d(tag, label + " size=" + list.size());
-        for (ShortcutInfo info: list) {
-            Log.d(tag, "   title=\"" + info.title + " icon=" + info.mIcon
-                    + " customIcon=" + info.customIcon);
+        if (iconResource != null) {
+            values.put(LauncherSettings.BaseLauncherColumns.ICON_PACKAGE,
+                    iconResource.packageName);
+            values.put(LauncherSettings.BaseLauncherColumns.ICON_RESOURCE,
+                    iconResource.resourceName);
         }
     }
 
@@ -287,17 +296,57 @@
         return usingLowResIcon && container >= 0 && rank >= FolderIcon.NUM_ITEMS_IN_PREVIEW;
     }
 
-    public static ShortcutInfo fromActivityInfo(LauncherActivityInfoCompat info, Context context) {
-        final ShortcutInfo shortcut = new ShortcutInfo();
-        shortcut.user = info.getUser();
-        shortcut.title = Utilities.trim(info.getLabel());
-        shortcut.contentDescription = UserManagerCompat.getInstance(context)
-                .getBadgedLabelForUser(info.getLabel(), info.getUser());
-        shortcut.customIcon = false;
-        shortcut.intent = AppInfo.makeLaunchIntent(context, info, info.getUser());
-        shortcut.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
-        shortcut.flags = AppInfo.initFlags(info);
-        return shortcut;
+    public void updateFromDeepShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
+        // {@link ShortcutInfoCompat#getActivity} can change during an update. Recreate the intent
+        intent = shortcutInfo.makeIntent(context);
+        title = shortcutInfo.getShortLabel();
+
+        CharSequence label = shortcutInfo.getLongLabel();
+        if (TextUtils.isEmpty(label)) {
+            label = shortcutInfo.getShortLabel();
+        }
+        contentDescription = UserManagerCompat.getInstance(context)
+                .getBadgedLabelForUser(label, user);
+        if (shortcutInfo.isEnabled()) {
+            isDisabled &= ~FLAG_DISABLED_BY_PUBLISHER;
+        } else {
+            isDisabled |= FLAG_DISABLED_BY_PUBLISHER;
+        }
+        disabledMessage = shortcutInfo.getDisabledMessage();
+
+        // TODO: Use cache for this
+        LauncherAppState launcherAppState = LauncherAppState.getInstance();
+        Drawable unbadgedDrawable = launcherAppState.getShortcutManager()
+                .getShortcutIconDrawable(shortcutInfo,
+                        launcherAppState.getInvariantDeviceProfile().fillResIconDpi);
+
+        IconCache cache = launcherAppState.getIconCache();
+        Bitmap unbadgedBitmap = unbadgedDrawable == null
+                ? cache.getDefaultIcon(UserHandleCompat.myUserHandle())
+                : Utilities.createScaledBitmapWithoutShadow(unbadgedDrawable, context);
+        setIcon(getBadgedIcon(unbadgedBitmap, shortcutInfo, cache, context));
+    }
+
+    protected Bitmap getBadgedIcon(Bitmap unbadgedBitmap, ShortcutInfoCompat shortcutInfo,
+            IconCache cache, Context context) {
+        unbadgedBitmap = Utilities.addShadowToIcon(unbadgedBitmap);
+        // Get the app info for the source activity.
+        AppInfo appInfo = new AppInfo();
+        appInfo.user = user;
+        appInfo.componentName = shortcutInfo.getActivity();
+        try {
+            cache.getTitleAndIcon(appInfo, shortcutInfo.getActivityInfo(context), false);
+        } catch (NullPointerException e) {
+            // This may happen when we fail to load the activity info. Worst case ignore badging.
+            return Utilities.badgeIconForUser(unbadgedBitmap, user, context);
+        }
+        return Utilities.badgeWithBitmap(unbadgedBitmap, appInfo.iconBitmap, context);
+    }
+
+    /** Returns the ShortcutInfo id associated with the deep shortcut. */
+    public String getDeepShortcutId() {
+        return itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT ?
+                getPromisedIntent().getStringExtra(ShortcutInfoCompat.EXTRA_SHORTCUT_ID) : null;
     }
 
     @Override
@@ -305,4 +354,3 @@
         return isDisabled != 0;
     }
 }
-
diff --git a/src/com/android/launcher3/SimpleOnStylusPressListener.java b/src/com/android/launcher3/SimpleOnStylusPressListener.java
new file mode 100644
index 0000000..6b97dce
--- /dev/null
+++ b/src/com/android/launcher3/SimpleOnStylusPressListener.java
@@ -0,0 +1,25 @@
+package com.android.launcher3;
+
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.launcher3.StylusEventHelper.StylusButtonListener;
+
+/**
+ * Simple listener that performs a long click on the view after a stylus button press.
+ */
+public class SimpleOnStylusPressListener implements StylusButtonListener {
+    private View mView;
+
+    public SimpleOnStylusPressListener(View view) {
+        mView = view;
+    }
+
+    public boolean onPressed(MotionEvent event) {
+        return mView.isLongClickable() && mView.performLongClick();
+    }
+
+    public boolean onReleased(MotionEvent event) {
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/StartupReceiver.java b/src/com/android/launcher3/StartupReceiver.java
deleted file mode 100644
index 65f913f..0000000
--- a/src/com/android/launcher3/StartupReceiver.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.android.launcher3;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-public class StartupReceiver extends BroadcastReceiver {
-
-    static final String SYSTEM_READY = "com.android.launcher3.SYSTEM_READY";
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        context.sendStickyBroadcast(new Intent(SYSTEM_READY));
-    }
-}
diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java
deleted file mode 100644
index d46c939..0000000
--- a/src/com/android/launcher3/Stats.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewParent;
-
-public class Stats {
-
-    /**
-     * Implemented by containers to provide a launch source for a given child.
-     */
-    public interface LaunchSourceProvider {
-        void fillInLaunchSourceData(View v, Bundle sourceData);
-    }
-
-    /**
-     * Helpers to add the source to a launch intent.
-     */
-    public static class LaunchSourceUtils {
-        /**
-         * Create a default bundle for LaunchSourceProviders to fill in their data.
-         */
-        public static Bundle createSourceData() {
-            Bundle sourceData = new Bundle();
-            sourceData.putString(SOURCE_EXTRA_CONTAINER, CONTAINER_HOMESCREEN);
-            // Have default container/sub container pages
-            sourceData.putInt(SOURCE_EXTRA_CONTAINER_PAGE, 0);
-            sourceData.putInt(SOURCE_EXTRA_SUB_CONTAINER_PAGE, 0);
-            return sourceData;
-        }
-
-        /**
-         * Finds the next launch source provider in the parents of the view hierarchy and populates
-         * the source data from that provider.
-         */
-        public static void populateSourceDataFromAncestorProvider(View v, Bundle sourceData) {
-            if (v == null) {
-                return;
-            }
-
-            Stats.LaunchSourceProvider provider = null;
-            ViewParent parent = v.getParent();
-            while (parent != null && parent instanceof View) {
-                if (parent instanceof Stats.LaunchSourceProvider) {
-                    provider = (Stats.LaunchSourceProvider) parent;
-                    break;
-                }
-                parent = parent.getParent();
-            }
-
-            if (provider != null) {
-                provider.fillInLaunchSourceData(v, sourceData);
-            } else if (LauncherAppState.isDogfoodBuild()) {
-                throw new RuntimeException("Expected LaunchSourceProvider");
-            }
-        }
-    }
-
-    private static final boolean DEBUG_BROADCASTS = false;
-
-    public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH";
-    public static final String EXTRA_INTENT = "intent";
-    public static final String EXTRA_CONTAINER = "container";
-    public static final String EXTRA_SCREEN = "screen";
-    public static final String EXTRA_CELLX = "cellX";
-    public static final String EXTRA_CELLY = "cellY";
-    public static final String EXTRA_SOURCE = "source";
-
-    public static final String SOURCE_EXTRA_CONTAINER = "container";
-    public static final String SOURCE_EXTRA_CONTAINER_PAGE = "container_page";
-    public static final String SOURCE_EXTRA_SUB_CONTAINER = "sub_container";
-    public static final String SOURCE_EXTRA_SUB_CONTAINER_PAGE = "sub_container_page";
-
-    public static final String CONTAINER_SEARCH_BOX = "search_box";
-    public static final String CONTAINER_ALL_APPS = "all_apps";
-    public static final String CONTAINER_HOMESCREEN = "homescreen"; // aka. Workspace
-    public static final String CONTAINER_HOTSEAT = "hotseat";
-
-    public static final String SUB_CONTAINER_FOLDER = "folder";
-    public static final String SUB_CONTAINER_ALL_APPS_A_Z = "a-z";
-    public static final String SUB_CONTAINER_ALL_APPS_PREDICTION = "prediction";
-    public static final String SUB_CONTAINER_ALL_APPS_SEARCH = "search";
-
-    private final Launcher mLauncher;
-    private final String mLaunchBroadcastPermission;
-
-    public Stats(Launcher launcher) {
-        mLauncher = launcher;
-        mLaunchBroadcastPermission =
-                launcher.getResources().getString(R.string.receive_launch_broadcasts_permission);
-
-        if (DEBUG_BROADCASTS) {
-            launcher.registerReceiver(
-                    new BroadcastReceiver() {
-                        @Override
-                        public void onReceive(Context context, Intent intent) {
-                            Log.v("Stats", "got broadcast: " + intent + " for launched intent: "
-                                    + intent.getStringExtra(EXTRA_INTENT));
-                        }
-                    },
-                    new IntentFilter(ACTION_LAUNCH),
-                    mLaunchBroadcastPermission,
-                    null
-            );
-        }
-    }
-
-    public void recordLaunch(View v, Intent intent, ShortcutInfo shortcut) {
-        intent = new Intent(intent);
-        intent.setSourceBounds(null);
-
-        final String flat = intent.toUri(0);
-        Intent broadcastIntent = new Intent(ACTION_LAUNCH).putExtra(EXTRA_INTENT, flat);
-
-        if (shortcut != null) {
-            broadcastIntent.putExtra(EXTRA_CONTAINER, shortcut.container)
-                    .putExtra(EXTRA_SCREEN, shortcut.screenId)
-                    .putExtra(EXTRA_CELLX, shortcut.cellX)
-                    .putExtra(EXTRA_CELLY, shortcut.cellY);
-        }
-
-        Bundle sourceExtras = LaunchSourceUtils.createSourceData();
-        LaunchSourceUtils.populateSourceDataFromAncestorProvider(v, sourceExtras);
-        broadcastIntent.putExtra(EXTRA_SOURCE, sourceExtras);
-
-        String[] packages = mLauncher.getResources().getStringArray(R.array.launch_broadcast_targets);
-        for(String p: packages) {
-            broadcastIntent.setPackage(p);
-            mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/StylusEventHelper.java b/src/com/android/launcher3/StylusEventHelper.java
index e03273a..d5fc0fa 100644
--- a/src/com/android/launcher3/StylusEventHelper.java
+++ b/src/com/android/launcher3/StylusEventHelper.java
@@ -6,55 +6,82 @@
 
 /**
  * Helper for identifying when a stylus touches a view while the primary stylus button is pressed.
- * This can occur in {@value MotionEvent#ACTION_DOWN} or {@value MotionEvent#ACTION_MOVE}. On a
- * stylus button press this performs the view's {@link View#performLongClick()} method, if the view
- * is long clickable.
+ * This can occur in {@value MotionEvent#ACTION_DOWN} or {@value MotionEvent#ACTION_MOVE}.
  */
 public class StylusEventHelper {
-    private boolean mIsButtonPressed;
-    private View mView;
-
-    public StylusEventHelper(View view) {
-        mView = view;
-    }
 
     /**
-     * Call this in onTouchEvent method of a view to identify a stylus button press and perform a
-     * long click (if the view is long clickable).
-     *
-     * @param event The event to check for a stylus button press.
-     * @return Whether a stylus event occurred and was handled.
+     * Implement this interface to receive callbacks for a stylus button press and release.
      */
-    public boolean checkAndPerformStylusEvent(MotionEvent event) {
-        final float slop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
+    public interface StylusButtonListener {
+        /**
+         * Called when the stylus button is pressed.
+         *
+         * @param event The MotionEvent that the button press occurred for.
+         * @return Whether the event was handled.
+         */
+        public boolean onPressed(MotionEvent event);
 
-        if (!mView.isLongClickable()) {
-            // We don't do anything unless the view is long clickable.
-            return false;
+        /**
+         * Called when the stylus button is released after a button press. This is also called if
+         * the event is canceled or the stylus is lifted off the screen.
+         *
+         * @param event The MotionEvent the button release occurred for.
+         * @return Whether the event was handled.
+         */
+        public boolean onReleased(MotionEvent event);
+    }
+
+    private boolean mIsButtonPressed;
+    private View mView;
+    private StylusButtonListener mListener;
+    private final float mSlop;
+
+    /**
+     * Constructs a helper for listening to stylus button presses and releases. Ensure that {
+     * {@link #onMotionEvent(MotionEvent)} and {@link #onGenericMotionEvent(MotionEvent)} are called on
+     * the helper to correctly identify stylus events.
+     *
+     * @param listener The listener to call for stylus events.
+     * @param view Optional view associated with the touch events.
+     */
+    public StylusEventHelper(StylusButtonListener listener, View view) {
+        mListener = listener;
+        mView = view;
+        if (mView != null) {
+            mSlop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
+        } else {
+            mSlop = ViewConfiguration.getTouchSlop();
         }
+    }
 
+    public boolean onMotionEvent(MotionEvent event) {
         final boolean stylusButtonPressed = isStylusButtonPressed(event);
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
-                mIsButtonPressed = false;
-                if (stylusButtonPressed && mView.performLongClick()) {
-                    mIsButtonPressed = true;
-                    return true;
+                mIsButtonPressed = stylusButtonPressed;
+                if (mIsButtonPressed) {
+                    return mListener.onPressed(event);
                 }
                 break;
             case MotionEvent.ACTION_MOVE:
-                if (Utilities.pointInView(mView, event.getX(), event.getY(), slop)) {
-                    if (!mIsButtonPressed && stylusButtonPressed && mView.performLongClick()) {
-                        mIsButtonPressed = true;
-                        return true;
-                    } else if (mIsButtonPressed && !stylusButtonPressed) {
-                        mIsButtonPressed = false;
-                    }
+                if (!Utilities.pointInView(mView, event.getX(), event.getY(), mSlop)) {
+                    return false;
+                }
+                if (!mIsButtonPressed && stylusButtonPressed) {
+                    mIsButtonPressed = true;
+                    return mListener.onPressed(event);
+                } else if (mIsButtonPressed && !stylusButtonPressed) {
+                    mIsButtonPressed = false;
+                    return mListener.onReleased(event);
                 }
                 break;
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
-                mIsButtonPressed = false;
+                if (mIsButtonPressed) {
+                    mIsButtonPressed = false;
+                    return mListener.onReleased(event);
+                }
                 break;
         }
         return false;
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 955d401..9153943 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -3,14 +3,16 @@
 import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.UserManager;
 import android.util.AttributeSet;
 import android.util.Pair;
-import com.android.launcher3.R;
+import android.widget.Toast;
+
 import com.android.launcher3.compat.UserHandleCompat;
-import com.android.launcher3.util.Thunk;
 
 public class UninstallDropTarget extends ButtonDropTarget {
 
@@ -32,7 +34,7 @@
     }
 
     @Override
-    protected boolean supportsDrop(DragSource source, Object info) {
+    protected boolean supportsDrop(DragSource source, ItemInfo info) {
         return supportsDrop(getContext(), info);
     }
 
@@ -72,56 +74,89 @@
     @Override
     public void onDrop(DragObject d) {
         // Differ item deletion
-        if (d.dragSource instanceof UninstallSource) {
-            ((UninstallSource) d.dragSource).deferCompleteDropAfterUninstallActivity();
+        if (d.dragSource instanceof DropTargetSource) {
+            ((DropTargetSource) d.dragSource).deferCompleteDropAfterUninstallActivity();
         }
         super.onDrop(d);
     }
 
     @Override
     void completeDrop(final DragObject d) {
-        final Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(d.dragInfo);
-        final UserHandleCompat user = ((ItemInfo) d.dragInfo).user;
-        if (startUninstallActivity(mLauncher, d.dragInfo)) {
+        DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
+                ? (DropTargetResultCallback) d.dragSource : null;
+        startUninstallActivity(mLauncher, d.dragInfo, callback);
+    }
 
+    public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) {
+        return startUninstallActivity(launcher, info, null);
+    }
+
+    public static boolean startUninstallActivity(
+            final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) {
+        Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(info);
+        ComponentName cn = componentInfo.first;
+
+        final boolean isUninstallable;
+        if ((componentInfo.second & AppInfo.DOWNLOADED_FLAG) == 0) {
+            // System applications cannot be installed. For now, show a toast explaining that.
+            // We may give them the option of disabling apps this way.
+            Toast.makeText(launcher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
+            isUninstallable = false;
+        } else {
+            Intent intent = new Intent(Intent.ACTION_DELETE,
+                    Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
+                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            info.user.addToIntent(intent, Intent.EXTRA_USER);
+            launcher.startActivity(intent);
+            isUninstallable = true;
+        }
+        if (callback != null) {
+            sendUninstallResult(
+                    launcher, isUninstallable, componentInfo.first, info.user, callback);
+        }
+        return isUninstallable;
+    }
+
+    /**
+     * Notifies the {@param callback} whether the uninstall was successful or not.
+     *
+     * Since there is no direct callback for an uninstall request, we check the package existence
+     * when the launch resumes next time. This assumes that the uninstall activity will finish only
+     * after the task is completed
+     */
+    protected static void sendUninstallResult(
+            final Launcher launcher, boolean activityStarted,
+            final ComponentName cn, final UserHandleCompat user,
+            final DropTargetResultCallback callback) {
+        if (activityStarted)  {
             final Runnable checkIfUninstallWasSuccess = new Runnable() {
                 @Override
                 public void run() {
-                    String packageName = componentInfo.first.getPackageName();
+                    String packageName = cn.getPackageName();
                     boolean uninstallSuccessful = !AllAppsList.packageHasActivities(
-                            getContext(), packageName, user);
-                    sendUninstallResult(d.dragSource, uninstallSuccessful);
+                            launcher, packageName, user);
+                    callback.onDragObjectRemoved(uninstallSuccessful);
                 }
             };
-            mLauncher.addOnResumeCallback(checkIfUninstallWasSuccess);
+            launcher.addOnResumeCallback(checkIfUninstallWasSuccess);
         } else {
-            sendUninstallResult(d.dragSource, false);
+            callback.onDragObjectRemoved(false);
         }
     }
 
-    public static boolean startUninstallActivity(Launcher launcher, Object info) {
-        final Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(info);
-        final UserHandleCompat user = ((ItemInfo) info).user;
-        return launcher.startApplicationUninstallActivity(
-                componentInfo.first, componentInfo.second, user);
-    }
-
-    @Thunk void sendUninstallResult(DragSource target, boolean result) {
-        if (target instanceof UninstallSource) {
-            ((UninstallSource) target).onUninstallActivityReturned(result);
-        }
+    public interface DropTargetResultCallback {
+        /**
+         * A drag operation was complete.
+         * @param isRemoved true if the drag object should be removed, false otherwise.
+         */
+        void onDragObjectRemoved(boolean isRemoved);
     }
 
     /**
      * Interface defining an object that can provide uninstallable drag objects.
      */
-    public static interface UninstallSource {
-
-        /**
-         * A pending uninstall operation was complete.
-         * @param result true if uninstall was successful, false otherwise.
-         */
-        void onUninstallActivityReturned(boolean result);
+    public interface DropTargetSource extends DropTargetResultCallback {
 
         /**
          * Indicates that an uninstall request are made and the actual result may come
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 9fdcd33..b0e096a 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -18,10 +18,7 @@
 
 import android.annotation.TargetApi;
 import android.app.Activity;
-import android.app.SearchManager;
 import android.app.WallpaperManager;
-import android.appwidget.AppWidgetManager;
-import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
@@ -33,6 +30,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -42,14 +40,13 @@
 import android.graphics.Paint;
 import android.graphics.PaintFlagsDrawFilter;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.PaintDrawable;
 import android.os.Build;
-import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.PowerManager;
-import android.os.Process;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
@@ -59,16 +56,24 @@
 import android.util.Pair;
 import android.util.SparseArray;
 import android.util.TypedValue;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.Toast;
 
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.graphics.ShadowGenerator;
 import com.android.launcher3.util.IconNormalizer;
 
 import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
 import java.io.IOException;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Locale;
 import java.util.Set;
 import java.util.concurrent.Executor;
@@ -101,8 +106,16 @@
     private static final int[] sLoc0 = new int[2];
     private static final int[] sLoc1 = new int[2];
 
-    // TODO: use Build.VERSION_CODES when available
-    public static final boolean ATLEAST_MARSHMALLOW = Build.VERSION.SDK_INT >= 23;
+    public static boolean isNycMR1OrAbove() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1;
+    }
+
+    public static boolean isNycOrAbove() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
+    }
+
+    public static final boolean ATLEAST_MARSHMALLOW =
+            Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
 
     public static final boolean ATLEAST_LOLLIPOP_MR1 =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1;
@@ -119,17 +132,8 @@
     public static final boolean ATLEAST_JB_MR2 =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2;
 
-    public static boolean isNycOrAbove() {
-        // TODO: Replace using reflection with looking at the API version once
-        // Build.VERSION.SDK_INT gets bumped to 24. b/22942492.
-        try {
-            View.class.getDeclaredField("DRAG_FLAG_OPAQUE");
-            // View.DRAG_FLAG_OPAQUE doesn't exist in M-release, so it's an indication of N+.
-            return true;
-        } catch (NoSuchFieldException e) {
-            return false;
-        }
-    }
+    // An intent extra to indicate the horizontal scroll of the wallpaper.
+    public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
 
     // These values are same as that in {@link AsyncTask}.
     private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
@@ -150,23 +154,20 @@
     }
 
     public static boolean isAllowRotationPrefEnabled(Context context) {
-        boolean allowRotationPref = false;
+        return getPrefs(context).getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
+                getAllowRotationDefaultValue(context));
+    }
+
+    public static boolean getAllowRotationDefaultValue(Context context) {
         if (isNycOrAbove()) {
             // If the device was scaled, used the original dimensions to determine if rotation
             // is allowed of not.
-            try {
-                // TODO: Use the actual field when the API is finalized.
-                int originalDensity =
-                        DisplayMetrics.class.getField("DENSITY_DEVICE_STABLE").getInt(null);
-                Resources res = context.getResources();
-                int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
-                        * res.getDisplayMetrics().densityDpi / originalDensity;
-                allowRotationPref = originalSmallestWidth >= 600;
-            } catch (Exception e) {
-                // Ignore
-            }
+            Resources res = context.getResources();
+            int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
+                    * res.getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEVICE_STABLE;
+            return originalSmallestWidth >= 600;
         }
-        return getPrefs(context).getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, allowRotationPref);
+        return false;
     }
 
     public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) {
@@ -222,12 +223,19 @@
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     public static Bitmap createBadgedIconBitmap(
             Drawable icon, UserHandleCompat user, Context context) {
-        float scale = FeatureFlags.LAUNCHER3_ICON_NORMALIZATION ?
-                IconNormalizer.getInstance().getScale(icon) : 1;
+        float scale = FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ?
+                1 : IconNormalizer.getInstance().getScale(icon, null);
         Bitmap bitmap = createIconBitmap(icon, context, scale);
+        return badgeIconForUser(bitmap, user, context);
+    }
+
+    /**
+     * Badges the provided icon with the user badge if required.
+     */
+    public static Bitmap badgeIconForUser(Bitmap icon,  UserHandleCompat user, Context context) {
         if (Utilities.ATLEAST_LOLLIPOP && user != null
                 && !UserHandleCompat.myUserHandle().equals(user)) {
-            BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
+            BitmapDrawable drawable = new FixedSizeBitmapDrawable(icon);
             Drawable badged = context.getPackageManager().getUserBadgedIcon(
                     drawable, user.getUser());
             if (badged instanceof BitmapDrawable) {
@@ -236,11 +244,48 @@
                 return createIconBitmap(badged, context);
             }
         } else {
-            return bitmap;
+            return icon;
         }
     }
 
     /**
+     * Creates a normalized bitmap suitable for the all apps view. The bitmap is also visually
+     * normalized with other icons and has enough spacing to add shadow.
+     */
+    public static Bitmap createScaledBitmapWithoutShadow(Drawable icon, Context context) {
+        RectF iconBounds = new RectF();
+        float scale = FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ?
+                1 : IconNormalizer.getInstance().getScale(icon, iconBounds);
+        scale = Math.min(scale, ShadowGenerator.getScaleForBounds(iconBounds));
+        return createIconBitmap(icon, context, scale);
+    }
+
+    /**
+     * Adds a shadow to the provided icon. It assumes that the icon has already been scaled using
+     * {@link #createScaledBitmapWithoutShadow(Drawable, Context)}
+     */
+    public static Bitmap addShadowToIcon(Bitmap icon) {
+        return ShadowGenerator.getInstance().recreateIcon(icon);
+    }
+
+    /**
+     * Adds the {@param badge} on top of {@param srcTgt} using the badge dimensions.
+     */
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    public static Bitmap badgeWithBitmap(Bitmap srcTgt, Bitmap badge, Context context) {
+        int badgeSize = context.getResources().getDimensionPixelSize(R.dimen.profile_badge_size);
+        synchronized (sCanvas) {
+            sCanvas.setBitmap(srcTgt);
+            sCanvas.drawBitmap(badge, new Rect(0, 0, badge.getWidth(), badge.getHeight()),
+                    new Rect(srcTgt.getWidth() - badgeSize,
+                            srcTgt.getHeight() - badgeSize, srcTgt.getWidth(), srcTgt.getHeight()),
+                    new Paint(Paint.FILTER_BITMAP_FLAG));
+            sCanvas.setBitmap(null);
+        }
+        return srcTgt;
+    }
+
+    /**
      * Returns a bitmap suitable for the all apps view.
      */
     public static Bitmap createIconBitmap(Drawable icon, Context context) {
@@ -322,7 +367,7 @@
      * coordinates.
      *
      * @param descendant The descendant to which the passed coordinate is relative.
-     * @param root The root view to make the coordinates relative to.
+     * @param ancestor The root view to make the coordinates relative to.
      * @param coord The coordinate that we want mapped.
      * @param includeRootScroll Whether or not to account for the scroll of the descendant:
      *          sometimes this is relevant as in a child's coordinates within the descendant.
@@ -330,43 +375,34 @@
      *         this scale factor is assumed to be equal in X and Y, and so if at any point this
      *         assumption fails, we will need to return a pair of scale factors.
      */
-    public static float getDescendantCoordRelativeToParent(View descendant, View root,
-                                                           int[] coord, boolean includeRootScroll) {
-        ArrayList<View> ancestorChain = new ArrayList<View>();
-
+    public static float getDescendantCoordRelativeToAncestor(
+            View descendant, View ancestor, int[] coord, boolean includeRootScroll) {
         float[] pt = {coord[0], coord[1]};
-
-        View v = descendant;
-        while(v != root && v != null) {
-            ancestorChain.add(v);
-            v = (View) v.getParent();
-        }
-        ancestorChain.add(root);
-
         float scale = 1.0f;
-        int count = ancestorChain.size();
-        for (int i = 0; i < count; i++) {
-            View v0 = ancestorChain.get(i);
+        View v = descendant;
+        while(v != ancestor && v != null) {
             // For TextViews, scroll has a meaning which relates to the text position
             // which is very strange... ignore the scroll.
-            if (v0 != descendant || includeRootScroll) {
-                pt[0] -= v0.getScrollX();
-                pt[1] -= v0.getScrollY();
+            if (v != descendant || includeRootScroll) {
+                pt[0] -= v.getScrollX();
+                pt[1] -= v.getScrollY();
             }
 
-            v0.getMatrix().mapPoints(pt);
-            pt[0] += v0.getLeft();
-            pt[1] += v0.getTop();
-            scale *= v0.getScaleX();
+            v.getMatrix().mapPoints(pt);
+            pt[0] += v.getLeft();
+            pt[1] += v.getTop();
+            scale *= v.getScaleX();
+
+            v = (View) v.getParent();
         }
 
-        coord[0] = (int) Math.round(pt[0]);
-        coord[1] = (int) Math.round(pt[1]);
+        coord[0] = Math.round(pt[0]);
+        coord[1] = Math.round(pt[1]);
         return scale;
     }
 
     /**
-     * Inverse of {@link #getDescendantCoordRelativeToParent(View, View, int[], boolean)}.
+     * Inverse of {@link #getDescendantCoordRelativeToAncestor(View, View, int[], boolean)}.
      */
     public static float mapCoordInSelfToDescendent(View descendant, View root,
                                                    int[] coord) {
@@ -416,13 +452,28 @@
                 localY < (v.getHeight() + slop);
     }
 
-    public static void scaleRect(Rect r, float scale) {
-        if (scale != 1.0f) {
-            r.left = (int) (r.left * scale + 0.5f);
-            r.top = (int) (r.top * scale + 0.5f);
-            r.right = (int) (r.right * scale + 0.5f);
-            r.bottom = (int) (r.bottom * scale + 0.5f);
-        }
+    /** Translates MotionEvents from src's coordinate system to dst's. */
+    public static void translateEventCoordinates(View src, View dst, MotionEvent dstEvent) {
+        toGlobalMotionEvent(src, dstEvent);
+        toLocalMotionEvent(dst, dstEvent);
+    }
+
+    /**
+     * Emulates View.toGlobalMotionEvent(). This implementation does not handle transformations
+     * (scaleX, scaleY, etc).
+     */
+    private static void toGlobalMotionEvent(View view, MotionEvent event) {
+        view.getLocationOnScreen(sLoc0);
+        event.offsetLocation(sLoc0[0], sLoc0[1]);
+    }
+
+    /**
+     * Emulates View.toLocalMotionEvent(). This implementation does not handle transformations
+     * (scaleX, scaleY, etc).
+     */
+    private static void toLocalMotionEvent(View view, MotionEvent event) {
+        view.getLocationOnScreen(sLoc0);
+        event.offsetLocation(-sLoc0[0], -sLoc0[1]);
     }
 
     public static int[] getCenterDeltaInScreenSpace(View v0, View v1, int[] delta) {
@@ -445,11 +496,18 @@
     }
 
     public static void scaleRectAboutCenter(Rect r, float scale) {
-        int cx = r.centerX();
-        int cy = r.centerY();
-        r.offset(-cx, -cy);
-        Utilities.scaleRect(r, scale);
-        r.offset(cx, cy);
+        if (scale != 1.0f) {
+            int cx = r.centerX();
+            int cy = r.centerY();
+            r.offset(-cx, -cy);
+
+            r.left = (int) (r.left * scale + 0.5f);
+            r.top = (int) (r.top * scale + 0.5f);
+            r.right = (int) (r.right * scale + 0.5f);
+            r.bottom = (int) (r.bottom * scale + 0.5f);
+
+            r.offset(cx, cy);
+        }
     }
 
     public static void startActivityForResultSafely(
@@ -592,49 +650,6 @@
         return null;
     }
 
-    @TargetApi(Build.VERSION_CODES.KITKAT)
-    public static boolean isViewAttachedToWindow(View v) {
-        if (ATLEAST_KITKAT) {
-            return v.isAttachedToWindow();
-        } else {
-            // A proxy call which returns null, if the view is not attached to the window.
-            return v.getKeyDispatcherState() != null;
-        }
-    }
-
-    /**
-     * Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}
-     * provided by the same package which is set to be global search activity.
-     * If widgetCategory is not supported, or no such widget is found, returns the first widget
-     * provided by the package.
-     */
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    public static AppWidgetProviderInfo getSearchWidgetProvider(Context context) {
-        SearchManager searchManager =
-                (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
-        ComponentName searchComponent = searchManager.getGlobalSearchActivity();
-        if (searchComponent == null) return null;
-        String providerPkg = searchComponent.getPackageName();
-
-        AppWidgetProviderInfo defaultWidgetForSearchPackage = null;
-
-        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
-        for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
-            if (info.provider.getPackageName().equals(providerPkg)) {
-                if (ATLEAST_JB_MR1) {
-                    if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
-                        return info;
-                    } else if (defaultWidgetForSearchPackage == null) {
-                        defaultWidgetForSearchPackage = info;
-                    }
-                } else {
-                    return info;
-                }
-            }
-        }
-        return defaultWidgetForSearchPackage;
-    }
-
     /**
      * Compresses the bitmap to a byte array for serialization.
      */
@@ -655,39 +670,6 @@
     }
 
     /**
-     * Find the first vacant cell, if there is one.
-     *
-     * @param vacant Holds the x and y coordinate of the vacant cell
-     * @param spanX Horizontal cell span.
-     * @param spanY Vertical cell span.
-     *
-     * @return true if a vacant cell was found
-     */
-    public static boolean findVacantCell(int[] vacant, int spanX, int spanY,
-            int xCount, int yCount, boolean[][] occupied) {
-
-        for (int y = 0; (y + spanY) <= yCount; y++) {
-            for (int x = 0; (x + spanX) <= xCount; x++) {
-                boolean available = !occupied[x][y];
-                out:            for (int i = x; i < x + spanX; i++) {
-                    for (int j = y; j < y + spanY; j++) {
-                        available = available && !occupied[i][j];
-                        if (!available) break out;
-                    }
-                }
-
-                if (available) {
-                    vacant[0] = x;
-                    vacant[1] = y;
-                    return true;
-                }
-            }
-        }
-
-        return false;
-    }
-
-    /**
      * Trims the string, removing all whitespace at the beginning and end of the string.
      * Non-breaking whitespaces are also removed.
      */
@@ -704,11 +686,11 @@
     /**
      * Calculates the height of a given string at a specific text size.
      */
-    public static float calculateTextHeight(float textSizePx) {
+    public static int calculateTextHeight(float textSizePx) {
         Paint p = new Paint();
         p.setTextSize(textSizePx);
         Paint.FontMetrics fm = p.getFontMetrics();
-        return -fm.top + fm.bottom;
+        return (int) Math.ceil(fm.bottom - fm.top);
     }
 
     /**
@@ -736,13 +718,6 @@
                 (res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
     }
 
-    public static void assertWorkerThread() {
-        if (LauncherAppState.isDogfoodBuild() &&
-                (LauncherModel.sWorkerThread.getThreadId() != Process.myTid())) {
-            throw new IllegalStateException();
-        }
-    }
-
     /**
      * Returns true if the intent is a valid launch intent for a launcher activity of an app.
      * This is used to identify shortcuts which are different from the ones exposed by the
@@ -787,6 +762,40 @@
         return String.format(Locale.ENGLISH, "%s IN (%s)", columnName, TextUtils.join(", ", values));
     }
 
+    public static boolean isBootCompleted() {
+        return "1".equals(getSystemProperty("sys.boot_completed", "1"));
+    }
+
+    public static String getSystemProperty(String property, String defaultValue) {
+        try {
+            Class clazz = Class.forName("android.os.SystemProperties");
+            Method getter = clazz.getDeclaredMethod("get", String.class);
+            String value = (String) getter.invoke(null, property);
+            if (!TextUtils.isEmpty(value)) {
+                return value;
+            }
+        } catch (Exception e) {
+            Log.d(TAG, "Unable to read system properties");
+        }
+        return defaultValue;
+    }
+
+    /**
+     * Ensures that a value is within given bounds. Specifically:
+     * If value is less than lowerBound, return lowerBound; else if value is greater than upperBound,
+     * return upperBound; else return value unchanged.
+     */
+    public static int boundToRange(int value, int lowerBound, int upperBound) {
+        return Math.max(lowerBound, Math.min(value, upperBound));
+    }
+
+    /**
+     * @see #boundToRange(int, int, int).
+     */
+    public static float boundToRange(float value, float lowerBound, float upperBound) {
+        return Math.max(lowerBound, Math.min(value, upperBound));
+    }
+
     /**
      * Wraps a message with a TTS span, so that a different message is spoken than
      * what is getting displayed.
@@ -827,13 +836,50 @@
         if (isNycOrAbove()) {
             try {
                 WallpaperManager wm = context.getSystemService(WallpaperManager.class);
-                return (Boolean) wm.getClass().getDeclaredMethod("isWallpaperSettingAllowed")
+                return (Boolean) wm.getClass().getDeclaredMethod("isSetWallpaperAllowed")
                         .invoke(wm);
             } catch (Exception e) { }
         }
         return true;
     }
 
+    public static void closeSilently(Closeable c) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (IOException e) {
+                if (ProviderConfig.IS_DOGFOOD_BUILD) {
+                    Log.d(TAG, "Error closing", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns true if {@param original} contains all entries defined in {@param updates} and
+     * have the same value.
+     * The comparison uses {@link Object#equals(Object)} to compare the values.
+     */
+    public static boolean containsAll(Bundle original, Bundle updates) {
+        for (String key : updates.keySet()) {
+            Object value1 = updates.get(key);
+            Object value2 = original.get(key);
+            if (value1 == null) {
+                if (value2 != null) {
+                    return false;
+                }
+            } else if (!value1.equals(value2)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /** Returns whether the collection is null or empty. */
+    public static boolean isEmpty(Collection c) {
+        return c == null || c.isEmpty();
+    }
+
     /**
      * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size.
      * This allows the badging to be done based on the action bitmap size rather than
@@ -855,4 +901,22 @@
             return getBitmap().getWidth();
         }
     }
+
+    public static int getColorAccent(Context context) {
+        TypedArray ta = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
+        int colorAccent = ta.getColor(0, 0);
+        ta.recycle();
+        return colorAccent;
+    }
+
+    public static void sendCustomAccessibilityEvent(View target, int type, String text) {
+        AccessibilityManager accessibilityManager = (AccessibilityManager)
+                target.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+        if (accessibilityManager.isEnabled()) {
+            AccessibilityEvent event = AccessibilityEvent.obtain(type);
+            target.onInitializeAccessibilityEvent(event);
+            event.getText().add(text);
+            accessibilityManager.sendAccessibilityEvent(event);
+        }
+    }
 }
diff --git a/src/com/android/launcher3/WallpaperChangedReceiver.java b/src/com/android/launcher3/WallpaperChangedReceiver.java
deleted file mode 100644
index c24fbff..0000000
--- a/src/com/android/launcher3/WallpaperChangedReceiver.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-public class WallpaperChangedReceiver extends BroadcastReceiver {
-    public void onReceive(Context context, Intent data) {
-        LauncherAppState.getInstance().onWallpaperChanged();
-    }
-}
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 314dd8a..45e65b5 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -3,9 +3,10 @@
 import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.database.SQLException;
@@ -30,7 +31,9 @@
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.SQLiteCacheHelper;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.WidgetCell;
@@ -87,15 +90,14 @@
      * Generates the widget preview on {@link AsyncTask#THREAD_POOL_EXECUTOR}. Must be
      * called on UI thread
      *
-     * @param o either {@link LauncherAppWidgetProviderInfo} or {@link ResolveInfo}
      * @return a request id which can be used to cancel the request.
      */
-    public PreviewLoadRequest getPreview(final Object o, int previewWidth,
+    public PreviewLoadRequest getPreview(WidgetItem item, int previewWidth,
             int previewHeight, WidgetCell caller) {
         String size = previewWidth + "x" + previewHeight;
-        WidgetCacheKey key = getObjectKey(o, size);
+        WidgetCacheKey key = new WidgetCacheKey(item.componentName, item.user, size);
 
-        PreviewLoadTask task = new PreviewLoadTask(key, o, previewWidth, previewHeight, caller);
+        PreviewLoadTask task = new PreviewLoadTask(key, item, previewWidth, previewHeight, caller);
         task.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
         return new PreviewLoadRequest(task);
     }
@@ -135,19 +137,6 @@
         }
     }
 
-    private WidgetCacheKey getObjectKey(Object o, String size) {
-        // should cache the string builder
-        if (o instanceof LauncherAppWidgetProviderInfo) {
-            LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) o;
-            return new WidgetCacheKey(info.provider, mWidgetManager.getUser(info), size);
-        } else {
-            ResolveInfo info = (ResolveInfo) o;
-            return new WidgetCacheKey(
-                    new ComponentName(info.activityInfo.packageName, info.activityInfo.name),
-                    UserHandleCompat.myUserHandle(), size);
-        }
-    }
-
     @Thunk void writeToDb(WidgetCacheKey key, long[] versions, Bitmap preview) {
         ContentValues values = new ContentValues();
         values.put(CacheDb.COLUMN_COMPONENT, key.componentName.flattenToShortString());
@@ -180,30 +169,19 @@
      *   2. Any preview for an absent package is removed
      * This ensures that we remove entries for packages which changed while the launcher was dead.
      */
-    public void removeObsoletePreviews(ArrayList<Object> list) {
-        Utilities.assertWorkerThread();
+    public void removeObsoletePreviews(ArrayList<? extends ComponentKey> list) {
+        Preconditions.assertWorkerThread();
 
         LongSparseArray<HashSet<String>> validPackages = new LongSparseArray<>();
 
-        for (Object obj : list) {
-            final UserHandleCompat user;
-            final String pkg;
-            if (obj instanceof ResolveInfo) {
-                user = UserHandleCompat.myUserHandle();
-                pkg = ((ResolveInfo) obj).activityInfo.packageName;
-            } else {
-                LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) obj;
-                user = mWidgetManager.getUser(info);
-                pkg = info.provider.getPackageName();
-            }
-
-            final long userId = mUserManager.getSerialNumberForUser(user);
+        for (ComponentKey key : list) {
+            final long userId = mUserManager.getSerialNumberForUser(key.user);
             HashSet<String> packages = validPackages.get(userId);
             if (packages == null) {
                 packages = new HashSet<>();
                 validPackages.put(userId, packages);
             }
-            packages.add(pkg);
+            packages.add(key.componentName.getPackageName());
         }
 
         LongSparseArray<HashSet<String>> packagesToDelete = new LongSparseArray<>();
@@ -294,14 +272,14 @@
         return null;
     }
 
-    @Thunk Bitmap generatePreview(Launcher launcher, Object info, Bitmap recycle,
+    private Bitmap generatePreview(Launcher launcher, WidgetItem item, Bitmap recycle,
             int previewWidth, int previewHeight) {
-        if (info instanceof LauncherAppWidgetProviderInfo) {
-            return generateWidgetPreview(launcher, (LauncherAppWidgetProviderInfo) info,
+        if (item.widgetInfo != null) {
+            return generateWidgetPreview(launcher, item.widgetInfo,
                     previewWidth, recycle, null);
         } else {
-            return generateShortcutPreview(launcher,
-                    (ResolveInfo) info, previewWidth, previewHeight, recycle);
+            return generateShortcutPreview(launcher, item.activityInfo,
+                    previewWidth, previewHeight, recycle);
         }
     }
 
@@ -430,7 +408,7 @@
     }
 
     private Bitmap generateShortcutPreview(
-            Launcher launcher, ResolveInfo info, int maxWidth, int maxHeight, Bitmap preview) {
+            Launcher launcher, ActivityInfo info, int maxWidth, int maxHeight, Bitmap preview) {
         final Canvas c = new Canvas();
         if (preview == null) {
             preview = Bitmap.createBitmap(maxWidth, maxHeight, Config.ARGB_8888);
@@ -443,7 +421,7 @@
             c.drawColor(0, PorterDuff.Mode.CLEAR);
         }
 
-        Drawable icon = mutateOnMainThread(mIconCache.getFullResIcon(info.activityInfo));
+        Drawable icon = mutateOnMainThread(mIconCache.getFullResIcon(info));
         icon.setFilterBitmap(true);
 
         // Draw a desaturated/scaled version of the icon in the background as a watermark
@@ -499,7 +477,8 @@
             if (versions == null) {
                 versions = new long[2];
                 try {
-                    PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName, 0);
+                    PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName,
+                            PackageManager.GET_UNINSTALLED_PACKAGES);
                     versions[0] = info.versionCode;
                     versions[1] = info.lastUpdateTime;
                 } catch (NameNotFoundException e) {
@@ -548,14 +527,14 @@
 
     public class PreviewLoadTask extends AsyncTask<Void, Void, Bitmap> {
         @Thunk final WidgetCacheKey mKey;
-        private final Object mInfo;
+        private final WidgetItem mInfo;
         private final int mPreviewHeight;
         private final int mPreviewWidth;
         private final WidgetCell mCaller;
         @Thunk long[] mVersions;
         @Thunk Bitmap mBitmapToRecycle;
 
-        PreviewLoadTask(WidgetCacheKey key, Object info, int previewWidth,
+        PreviewLoadTask(WidgetCacheKey key, WidgetItem info, int previewWidth,
                 int previewHeight, WidgetCell caller) {
             mKey = key;
             mInfo = info;
@@ -605,7 +584,7 @@
                 // which would gets re-written next time.
                 mVersions = getPackageVersion(mKey.componentName.getPackageName());
 
-                Launcher launcher = (Launcher) mCaller.getContext();
+                Launcher launcher = Launcher.getLauncher(mCaller.getContext());
 
                 // it's not in the db... we need to generate it
                 preview = generatePreview(launcher, mInfo, unusedBitmap, mPreviewWidth, mPreviewHeight);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 4eb4d45..340177d 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -18,69 +18,76 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
 import android.app.WallpaperManager;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.SharedPreferences;
 import android.content.res.Resources;
-import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
-import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Property;
 import android.util.SparseArray;
-import android.view.Choreographer;
-import android.view.Display;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
-import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.widget.TextView;
 
-import com.android.launcher3.FolderIcon.FolderRingAnimator;
 import com.android.launcher3.Launcher.CustomContentCallbacks;
 import com.android.launcher3.Launcher.LauncherOverlay;
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.UninstallDropTarget.UninstallSource;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
+import com.android.launcher3.UninstallDropTarget.DropTargetSource;
+import com.android.launcher3.accessibility.AccessibileDragListenerAdapter;
+import com.android.launcher3.accessibility.OverviewAccessibilityDelegate;
 import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
 import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragScroller;
+import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.dragndrop.SpringLoadedDragController;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.graphics.DragPreviewProvider;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LongArrayMap;
+import com.android.launcher3.util.MultiStateAlphaController;
 import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.WallpaperUtils;
+import com.android.launcher3.util.VerticalFlingDetector;
+import com.android.launcher3.util.WallpaperOffsetInterpolator;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -90,18 +97,25 @@
 public class Workspace extends PagedView
         implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
         DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
-        Insettable, UninstallSource, AccessibilityDragSource, Stats.LaunchSourceProvider {
+        Insettable, DropTargetSource {
     private static final String TAG = "Launcher.Workspace";
 
     private static boolean ENFORCE_DRAG_EVENT_ORDER = false;
 
-    protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
-    protected static final int FADE_EMPTY_SCREEN_DURATION = 150;
+    private static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
+    private static final int FADE_EMPTY_SCREEN_DURATION = 150;
 
     private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
 
-    static final boolean MAP_NO_RECURSE = false;
-    static final boolean MAP_RECURSE = true;
+    private static final boolean MAP_NO_RECURSE = false;
+    private static final boolean MAP_RECURSE = true;
+
+    // The screen id used for the empty screen always present to the right.
+    public static final long EXTRA_EMPTY_SCREEN_ID = -201;
+    // The is the first screen. It is always present, even if its empty.
+    public static final long FIRST_SCREEN_ID = 0;
+
+    private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
 
     private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200;
     private long mTouchDownTime = -1;
@@ -109,23 +123,14 @@
 
     private LayoutTransition mLayoutTransition;
     @Thunk final WallpaperManager mWallpaperManager;
-    @Thunk IBinder mWindowToken;
-
-    private int mOriginalDefaultPage;
-    private int mDefaultPage;
 
     private ShortcutAndWidgetContainer mDragSourceInternal;
 
-    // The screen id used for the empty screen always present to the right.
-    final static long EXTRA_EMPTY_SCREEN_ID = -201;
-    private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
-
     @Thunk LongArrayMap<CellLayout> mWorkspaceScreens = new LongArrayMap<>();
     @Thunk ArrayList<Long> mScreenOrder = new ArrayList<Long>();
 
     @Thunk Runnable mRemoveEmptyScreenRunnable;
     @Thunk boolean mDeferRemoveExtraEmptyScreen = false;
-    @Thunk boolean mAddNewPageOnDrag = true;
 
     /**
      * CellInfo for the cell that is currently being dragged
@@ -139,9 +144,6 @@
     private int mDragOverX = -1;
     private int mDragOverY = -1;
 
-    static Rect mLandscapeCellLayoutMetrics = null;
-    static Rect mPortraitCellLayoutMetrics = null;
-
     CustomContentCallbacks mCustomContentCallbacks;
     boolean mCustomContentShowing;
     private float mLastCustomContentScrollProgress = -1f;
@@ -152,7 +154,7 @@
      */
     @Thunk CellLayout mDragTargetLayout = null;
     /**
-     * The CellLayout that we will show as glowing
+     * The CellLayout that we will show as highlighted
      */
     private CellLayout mDragOverlappingLayout = null;
 
@@ -167,42 +169,73 @@
 
     // These are temporary variables to prevent having to allocate a new object just to
     // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
-    private int[] mTempCell = new int[2];
-    private int[] mTempPt = new int[2];
-    private int[] mTempEstimate = new int[2];
+    private static final Rect sTempRect = new Rect();
+    private final int[] mTempXY = new int[2];
     @Thunk float[] mDragViewVisualCenter = new float[2];
     private float[] mTempCellLayoutCenterCoordinates = new float[2];
-    private Matrix mTempInverseMatrix = new Matrix();
+    private int[] mTempVisiblePagesRange = new int[2];
     private Matrix mTempMatrix = new Matrix();
 
     private SpringLoadedDragController mSpringLoadedDragController;
-    private float mSpringLoadedShrinkFactor;
     private float mOverviewModeShrinkFactor;
 
     // State variable that indicates whether the pages are small (ie when you're
     // in all apps or customize mode)
 
-    enum State {
-        NORMAL          (SearchDropTargetBar.State.SEARCH_BAR, false),
-        NORMAL_HIDDEN   (SearchDropTargetBar.State.INVISIBLE_TRANSLATED, false),
-        SPRING_LOADED   (SearchDropTargetBar.State.DROP_TARGET, false),
-        OVERVIEW        (SearchDropTargetBar.State.INVISIBLE, true),
-        OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE, true);
+    public enum State {
+        NORMAL          (false, false),
+        NORMAL_HIDDEN   (false, false),
+        SPRING_LOADED   (false, true),
+        OVERVIEW        (true, true),
+        OVERVIEW_HIDDEN (true, false);
 
-        public final SearchDropTargetBar.State searchDropTargetBarState;
         public final boolean shouldUpdateWidget;
+        public final boolean hasMultipleVisiblePages;
 
-        State(SearchDropTargetBar.State searchBarState, boolean shouldUpdateWidget) {
-            searchDropTargetBarState = searchBarState;
+        State(boolean shouldUpdateWidget, boolean hasMultipleVisiblePages) {
             this.shouldUpdateWidget = shouldUpdateWidget;
+            this.hasMultipleVisiblePages = hasMultipleVisiblePages;
         }
     }
 
+    // Direction used for moving the workspace and hotseat UI
+    public enum Direction {
+        X  (TRANSLATION_X),
+        Y  (TRANSLATION_Y);
+
+        private final Property<View, Float> viewProperty;
+
+        Direction(Property<View, Float> viewProperty) {
+            this.viewProperty = viewProperty;
+        }
+    }
+
+    private static final int HOTSEAT_STATE_ALPHA_INDEX = 2;
+
+    /**
+     * These values correspond to {@link Direction#X} & {@link Direction#Y}
+     */
+    private float[] mPageAlpha = new float[] {1, 1};
+    /**
+     * Hotseat alpha can be changed when moving horizontally, vertically, changing states.
+     * The values correspond to {@link Direction#X}, {@link Direction#Y} &
+     * {@link #HOTSEAT_STATE_ALPHA_INDEX} respectively.
+     */
+    private float[] mHotseatAlpha = new float[] {1, 1, 1};
+
+    public static final int QSB_ALPHA_INDEX_STATE_CHANGE = 0;
+    public static final int QSB_ALPHA_INDEX_Y_TRANSLATION = 1;
+    public static final int QSB_ALPHA_INDEX_PAGE_SCROLL = 2;
+    public static final int QSB_ALPHA_INDEX_OVERLAY_SCROLL = 3;
+
+
+    MultiStateAlphaController mQsbAlphaController;
+
+    @ViewDebug.ExportedProperty(category = "launcher")
     private State mState = State.NORMAL;
     private boolean mIsSwitchingState = false;
 
     boolean mAnimatingViewIntoPlace = false;
-    boolean mIsDragOccuring = false;
     boolean mChildrenLayersEnabled = true;
 
     private boolean mStripScreensOnPageStopMoving = false;
@@ -210,29 +243,22 @@
     /** Is the user is dragging an item near the edge of a page? */
     private boolean mInScrollArea = false;
 
-    private HolographicOutlineHelper mOutlineHelper;
-    @Thunk Bitmap mDragOutline = null;
-    private static final Rect sTempRect = new Rect();
-    private final int[] mTempXY = new int[2];
-    private int[] mTempVisiblePagesRange = new int[2];
-    public static final int DRAG_BITMAP_PADDING = 2;
+    private DragPreviewProvider mOutlineProvider = null;
+    public static final int DRAG_BITMAP_PADDING = DragPreviewProvider.DRAG_BITMAP_PADDING;
     private boolean mWorkspaceFadeInAdjacentScreens;
 
-    WallpaperOffsetInterpolator mWallpaperOffset;
-    @Thunk boolean mWallpaperIsLiveWallpaper;
-    @Thunk int mNumPagesForWallpaperParallax;
-    @Thunk float mLastSetWallpaperOffsetSteps = 0;
+    final WallpaperOffsetInterpolator mWallpaperOffset;
+    private boolean mUnlockWallpaperFromDefaultPageOnLayout;
 
     @Thunk Runnable mDelayedResizeRunnable;
     private Runnable mDelayedSnapToPageRunnable;
-    private Point mDisplaySize = new Point();
 
     // Variables relating to the creation of user folders by hovering shortcuts over shortcuts
     private static final int FOLDER_CREATION_TIMEOUT = 0;
     public static final int REORDER_TIMEOUT = 350;
     private final Alarm mFolderCreationAlarm = new Alarm();
     private final Alarm mReorderAlarm = new Alarm();
-    @Thunk FolderRingAnimator mDragFolderRingAnimator = null;
+    private FolderIcon.PreviewBackground mFolderCreateBg;
     private FolderIcon mDragOverFolderIcon = null;
     private boolean mCreateUserFolderOnDrop = false;
     private boolean mAddToExistingFolderOnDrop = false;
@@ -280,20 +306,17 @@
     float mLastOverlaySroll = 0;
     // Total over scrollX in the overlay direction.
     private int mUnboundedScrollX;
+    private boolean mForceDrawAdjacentPages = false;
     // Total over scrollX in the overlay direction.
     private float mOverlayTranslation;
+    private int mFirstPageScrollX;
+    private boolean mIgnoreQsbScroll;
 
     // Handles workspace state transitions
     private WorkspaceStateTransitionAnimation mStateTransitionAnimation;
 
     private AccessibilityDelegate mPagesAccessibilityDelegate;
-
-    private final Runnable mBindPages = new Runnable() {
-        @Override
-        public void run() {
-            mLauncher.getModel().bindRemainingSynchronousPages();
-        }
-    };
+    private OnStateChangeListener mOnStateChangeListener;
 
     /**
      * Used to inflate the Workspace from XML.
@@ -315,24 +338,16 @@
     public Workspace(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-        mOutlineHelper = HolographicOutlineHelper.obtain(context);
-
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
         mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);
         final Resources res = getResources();
         DeviceProfile grid = mLauncher.getDeviceProfile();
         mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
-        mFadeInAdjacentScreens = false;
         mWallpaperManager = WallpaperManager.getInstance(context);
 
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.Workspace, defStyle, 0);
-        mSpringLoadedShrinkFactor =
-                res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
+        mWallpaperOffset = new WallpaperOffsetInterpolator(this);
         mOverviewModeShrinkFactor =
                 res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f;
-        mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
-        a.recycle();
 
         setOnHierarchyChangeListener(this);
         setHapticFeedbackEnabled(false);
@@ -356,9 +371,14 @@
         }
     }
 
+    public void setOnStateChangeListener(OnStateChangeListener listener) {
+        mOnStateChangeListener = listener;
+    }
+
     // estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
     // dimension if unsuccessful
     public int[] estimateItemSize(ItemInfo itemInfo, boolean springLoaded) {
+        float shrinkFactor = mLauncher.getDeviceProfile().workspaceSpringLoadShrinkFactor;
         int[] size = new int[2];
         if (getChildCount() > 0) {
             // Use the first non-custom page to estimate the child position
@@ -367,8 +387,8 @@
             size[0] = r.width();
             size[1] = r.height();
             if (springLoaded) {
-                size[0] *= mSpringLoadedShrinkFactor;
-                size[1] *= mSpringLoadedShrinkFactor;
+                size[0] *= shrinkFactor;
+                size[1] *= shrinkFactor;
             }
             return size;
         } else {
@@ -385,26 +405,56 @@
     }
 
     @Override
-    public void onDragStart(final DragSource source, Object info, int dragAction) {
+    public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
         if (ENFORCE_DRAG_EVENT_ORDER) {
             enfoceDragParity("onDragStart", 0, 0);
         }
 
-        mIsDragOccuring = true;
+        if (mOutlineProvider != null) {
+            // The outline is used to visualize where the item will land if dropped
+            mOutlineProvider.generateDragOutline(mCanvas);
+        }
+
         updateChildrenLayersEnabled(false);
+        mLauncher.onDragStarted();
         mLauncher.lockScreenOrientation();
         mLauncher.onInteractionBegin();
         // Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
         InstallShortcutReceiver.enableInstallQueue();
 
-        if (mAddNewPageOnDrag) {
+        // Do not add a new page if it is a accessible drag which was not started by the workspace.
+        // We do not support accessibility drag from other sources and instead provide a direct
+        // action for move/add to homescreen.
+        // When a accessible drag is started by the folder, we only allow rearranging withing the
+        // folder.
+        boolean addNewPage = !(options.isAccessibleDrag && dragObject.dragSource != this);
+
+        if (addNewPage) {
             mDeferRemoveExtraEmptyScreen = false;
             addExtraEmptyScreenOnDrag();
-        }
-    }
 
-    public void setAddNewPageOnDrag(boolean addPage) {
-        mAddNewPageOnDrag = addPage;
+            if (dragObject.dragInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+                    && dragObject.dragSource != this) {
+                // When dragging a widget from different source, move to a page which has
+                // enough space to place this widget (after rearranging/resizing). We special case
+                // widgets as they cannot be placed inside a folder.
+                // Start at the current page and search right (on LTR) until finding a page with
+                // enough space. Since an empty screen is the furthest right, a page must be found.
+                int currentPage = getPageNearestToCenterOfScreen();
+                for (int pageIndex = currentPage; pageIndex < getPageCount(); pageIndex++) {
+                    CellLayout page = (CellLayout) getPageAt(pageIndex);
+                    if (page.hasReorderSolution(dragObject.dragInfo)) {
+                        setCurrentPage(pageIndex);
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
+            // Always enter the spring loaded mode
+            mLauncher.enterSpringLoadedDragMode();
+        }
     }
 
     public void deferRemoveExtraEmptyScreen() {
@@ -421,7 +471,6 @@
             removeExtraEmptyScreen(true, mDragSourceInternal != null);
         }
 
-        mIsDragOccuring = false;
         updateChildrenLayersEnabled(false);
         mLauncher.unlockScreenOrientation(false);
 
@@ -436,7 +485,7 @@
      * Initializes various states for this workspace.
      */
     protected void initWorkspace() {
-        mCurrentPage = mDefaultPage;
+        mCurrentPage = getDefaultPage();
         LauncherAppState app = LauncherAppState.getInstance();
         DeviceProfile grid = mLauncher.getDeviceProfile();
         mIconCache = app.getIconCache();
@@ -448,10 +497,6 @@
         setMinScale(mOverviewModeShrinkFactor);
         setupLayoutTransition();
 
-        mWallpaperOffset = new WallpaperOffsetInterpolator();
-        Display display = mLauncher.getWindowManager().getDefaultDisplay();
-        display.getSize(mDisplaySize);
-
         mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
 
         // Set the wallpaper dimensions when Launcher starts up
@@ -460,6 +505,17 @@
         setEdgeGlowColor(getResources().getColor(R.color.workspace_edge_effect_color));
     }
 
+    @Override
+    public void initParentViews(View parent) {
+        super.initParentViews(parent);
+        mPageIndicator.setAccessibilityDelegate(new OverviewAccessibilityDelegate());
+        mQsbAlphaController = new MultiStateAlphaController(mLauncher.getQsbContainer(), 4);
+    }
+
+    private int getDefaultPage() {
+        return numCustomPages();
+    }
+
     private void setupLayoutTransition() {
         // We want to show layout transitions when pages are deleted, to close the gap.
         mLayoutTransition = new LayoutTransition();
@@ -502,8 +558,9 @@
      */
     public Folder getOpenFolder() {
         DragLayer dragLayer = mLauncher.getDragLayer();
-        int count = dragLayer.getChildCount();
-        for (int i = 0; i < count; i++) {
+        // Iterate in reverse order. Folder is added later to the dragLayer,
+        // and will be one of the last views.
+        for (int i = dragLayer.getChildCount() - 1; i >= 0; i--) {
             View child = dragLayer.getChildAt(i);
             if (child instanceof Folder) {
                 Folder folder = (Folder) child;
@@ -518,6 +575,86 @@
         return mTouchState != TOUCH_STATE_REST;
     }
 
+    private int getEmbeddedQsbId() {
+        return mLauncher.getDeviceProfile().isVerticalBarLayout()
+                ? R.id.qsb_container : R.id.workspace_blocked_row;
+    }
+
+    /**
+     * Initializes and binds the first page
+     * @param qsb an exisitng qsb to recycle or null.
+     */
+    public void bindAndInitFirstWorkspaceScreen(View qsb) {
+        if (!FeatureFlags.QSB_ON_FIRST_SCREEN) {
+            return;
+        }
+        // Add the first page
+        CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, 0);
+        if (FeatureFlags.PULLDOWN_SEARCH) {
+            firstPage.setOnTouchListener(new VerticalFlingDetector(mLauncher) {
+                // detect fling when touch started from empty space
+                @Override
+                public boolean onTouch(View v, MotionEvent ev) {
+                    if (workspaceInModalState()) return false;
+                    if (shouldConsumeTouch(v)) return true;
+                    if (super.onTouch(v, ev)) {
+                        mLauncher.startSearch("", false, null, false);
+                        return true;
+                    }
+                    return false;
+                }
+            });
+            firstPage.setOnInterceptTouchListener(new VerticalFlingDetector(mLauncher) {
+                // detect fling when touch started from on top of the icons
+                @Override
+                public boolean onTouch(View v, MotionEvent ev) {
+                    if (shouldConsumeTouch(v)) return true;
+                    if (super.onTouch(v, ev)) {
+                        mLauncher.startSearch("", false, null, false);
+                        return true;
+                    }
+                    return false;
+                }
+            });
+        }
+        // Always add a QSB on the first screen.
+        if (qsb == null) {
+            // In transposed layout, we add the QSB in the Grid. As workspace does not touch the
+            // edges, we do not need a full width QSB.
+            qsb = mLauncher.getLayoutInflater().inflate(
+                    mLauncher.getDeviceProfile().isVerticalBarLayout()
+                            ? R.layout.qsb_container : R.layout.qsb_blocker_view,
+                    firstPage, false);
+        }
+
+        CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(), 1);
+        lp.canReorder = false;
+        if (!firstPage.addViewToCellLayout(qsb, 0, getEmbeddedQsbId(), lp, true)) {
+            Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        // Update the QSB to match the cell height. This is treating the QSB essentially as a child
+        // of workspace despite that it's not a true child.
+        // Note that it relies on the strict ordering of measuring the workspace before the QSB
+        // at the dragLayer level.
+        if (getChildCount() > 0) {
+            CellLayout firstPage = (CellLayout) getChildAt(0);
+            int cellHeight = firstPage.getCellHeight();
+
+            View qsbContainer = mLauncher.getQsbContainer();
+            ViewGroup.LayoutParams lp = qsbContainer.getLayoutParams();
+            if (cellHeight > 0 && lp.height != cellHeight) {
+                lp.height = cellHeight;
+                qsbContainer.setLayoutParams(lp);
+            }
+        }
+    }
+
     public void removeAllWorkspaceScreens() {
         // Disable all layout transitions before removing all pages to ensure that we don't get the
         // transition animations competing with us changing the scroll when we add pages or the
@@ -530,30 +667,39 @@
             removeCustomContentPage();
         }
 
+        // Recycle the QSB widget
+        View qsb = findViewById(getEmbeddedQsbId());
+        if (qsb != null) {
+            ((ViewGroup) qsb.getParent()).removeView(qsb);
+        }
+
         // Remove the pages and clear the screen models
         removeAllViews();
         mScreenOrder.clear();
         mWorkspaceScreens.clear();
 
+        // Ensure that the first page is always present
+        bindAndInitFirstWorkspaceScreen(qsb);
+
         // Re-enable the layout transitions
         enableLayoutTransitions();
     }
 
-    public long insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
+    public void insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) {
         // Find the index to insert this view into.  If the empty screen exists, then
         // insert it before that.
         int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
         if (insertIndex < 0) {
             insertIndex = mScreenOrder.size();
         }
-        return insertNewWorkspaceScreen(screenId, insertIndex);
+        insertNewWorkspaceScreen(screenId, insertIndex);
     }
 
-    public long insertNewWorkspaceScreen(long screenId) {
-        return insertNewWorkspaceScreen(screenId, getChildCount());
+    public void insertNewWorkspaceScreen(long screenId) {
+        insertNewWorkspaceScreen(screenId, getChildCount());
     }
 
-    public long insertNewWorkspaceScreen(long screenId, int insertIndex) {
+    public CellLayout insertNewWorkspaceScreen(long screenId, int insertIndex) {
         if (mWorkspaceScreens.containsKey(screenId)) {
             throw new RuntimeException("Screen id " + screenId + " already exists!");
         }
@@ -562,7 +708,6 @@
         // created CellLayout.
         CellLayout newScreen = (CellLayout) mLauncher.getLayoutInflater().inflate(
                         R.layout.workspace_screen, this, false /* attachToRoot */);
-
         newScreen.setOnLongClickListener(mLongClickListener);
         newScreen.setOnClickListener(mLauncher);
         newScreen.setSoundEffectsEnabled(false);
@@ -570,12 +715,11 @@
         mScreenOrder.add(insertIndex, screenId);
         addView(newScreen, insertIndex);
 
-        LauncherAccessibilityDelegate delegate =
-                LauncherAppState.getInstance().getAccessibilityDelegate();
-        if (delegate != null && delegate.isInAccessibleDrag()) {
+        if (mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
             newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
         }
-        return screenId;
+
+        return newScreen;
     }
 
     public void createCustomContentContainer() {
@@ -592,9 +736,6 @@
 
         addFullScreenPage(customScreen);
 
-        // Ensure that the current page and default page are maintained.
-        mDefaultPage = mOriginalDefaultPage + 1;
-
         // Update the custom content hint
         if (mRestorePage != INVALID_RESTORE_PAGE) {
             mRestorePage = mRestorePage + 1;
@@ -620,9 +761,6 @@
 
         mCustomContentCallbacks = null;
 
-        // Ensure that the current page and default page are maintained.
-        mDefaultPage = mOriginalDefaultPage - 1;
-
         // Update the custom content hint
         if (mRestorePage != INVALID_RESTORE_PAGE) {
             mRestorePage = mRestorePage - 1;
@@ -701,7 +839,6 @@
     private void convertFinalScreenToEmptyScreenIfNecessary() {
         if (mLauncher.isWorkspaceLoading()) {
             // Invalid and dangerous operation if workspace is loading
-            Launcher.addDumpLog(TAG, "    - workspace loading, skip", true);
             return;
         }
 
@@ -734,7 +871,6 @@
             final int delay, final boolean stripEmptyScreens) {
         if (mLauncher.isWorkspaceLoading()) {
             // Don't strip empty screens if the workspace is still loading
-            Launcher.addDumpLog(TAG, "    - workspace loading, skip", true);
             return;
         }
 
@@ -790,6 +926,8 @@
                     if (stripEmptyScreens) {
                         stripEmptyScreens();
                     }
+                    // Update the page indicator to reflect the removed page.
+                    showPageIndicatorAtCurrentScroll();
                 }
             }
         };
@@ -820,7 +958,6 @@
     public long commitExtraEmptyScreen() {
         if (mLauncher.isWorkspaceLoading()) {
             // Invalid and dangerous operation if workspace is loading
-            Launcher.addDumpLog(TAG, "    - workspace loading, skip", true);
             return -1;
         }
 
@@ -829,15 +966,12 @@
         mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
         mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
 
-        long newId = LauncherAppState.getLauncherProvider().generateNewScreenId();
+        long newId = LauncherSettings.Settings.call(getContext().getContentResolver(),
+                LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
+                .getLong(LauncherSettings.Settings.EXTRA_VALUE);
         mWorkspaceScreens.put(newId, cl);
         mScreenOrder.add(newId);
 
-        // Update the page indicator marker
-        if (getPageIndicator() != null) {
-            getPageIndicator().updateMarker(index, getPageIndicatorMarker(index));
-        }
-
         // Update the model for the new screen
         mLauncher.getModel().updateWorkspaceScreenOrder(mLauncher, mScreenOrder);
 
@@ -845,8 +979,7 @@
     }
 
     public CellLayout getScreenWithId(long screenId) {
-        CellLayout layout = mWorkspaceScreens.get(screenId);
-        return layout;
+        return mWorkspaceScreens.get(screenId);
     }
 
     public long getIdForScreen(CellLayout layout) {
@@ -876,7 +1009,6 @@
         if (mLauncher.isWorkspaceLoading()) {
             // Don't strip empty screens if the workspace is still loading.
             // This is dangerous and can result in data loss.
-            Launcher.addDumpLog(TAG, "    - workspace loading, skip", true);
             return;
         }
 
@@ -891,13 +1023,14 @@
         for (int i = 0; i < total; i++) {
             long id = mWorkspaceScreens.keyAt(i);
             CellLayout cl = mWorkspaceScreens.valueAt(i);
-            if (id >= 0 && cl.getShortcutsAndWidgets().getChildCount() == 0) {
+            // FIRST_SCREEN_ID can never be removed.
+            if ((!FeatureFlags.QSB_ON_FIRST_SCREEN || id > FIRST_SCREEN_ID)
+                    && cl.getShortcutsAndWidgets().getChildCount() == 0) {
                 removeScreens.add(id);
             }
         }
 
-        LauncherAccessibilityDelegate delegate =
-                LauncherAppState.getInstance().getAccessibilityDelegate();
+        boolean isInAccessibleDrag = mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
 
         // We enforce at least one page to add new items to. In the case that we remove the last
         // such screen, we convert the last screen to the empty screen
@@ -914,7 +1047,7 @@
                     pageShift++;
                 }
 
-                if (delegate != null && delegate.isInAccessibleDrag()) {
+                if (isInAccessibleDrag) {
                     cl.enableAccessibleDrag(false, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
                 }
 
@@ -945,7 +1078,7 @@
 
     // At bind time, we use the rank (screenId) to compute x and y for hotseat items.
     // See implementation for parameter definition.
-    void addInScreenFromBind(View child, long container, long screenId, int x, int y,
+    public void addInScreenFromBind(View child, long container, long screenId, int x, int y,
             int spanX, int spanY) {
         addInScreen(child, container, screenId, x, y, spanX, spanY, false, true);
     }
@@ -1036,7 +1169,7 @@
             // TODO: This branch occurs when the workspace is adding views
             // outside of the defined grid
             // maybe we should be deleting these items from the LauncherModel?
-            Launcher.addDumpLog(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout", true);
+            Log.e(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
         }
 
         if (!(child instanceof Folder)) {
@@ -1056,6 +1189,10 @@
     @SuppressLint("ClickableViewAccessibility")
     @Override
     public boolean onTouch(View v, MotionEvent event) {
+        return shouldConsumeTouch(v);
+    }
+
+    private boolean shouldConsumeTouch(View v) {
         return (workspaceInModalState() || !isFinishedSwitchingState())
                 || (!workspaceInModalState() && indexOfChild(v) != mCurrentPage);
     }
@@ -1123,10 +1260,11 @@
             for (int j = 0; j < itemCount; j++) {
                 View v = swc.getChildAt(j);
 
-                if (v != null  && v.getTag() instanceof LauncherAppWidgetInfo) {
+                if (v instanceof LauncherAppWidgetHostView
+                        && v.getTag() instanceof LauncherAppWidgetInfo) {
                     LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
-                    LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) info.hostView;
-                    if (lahv != null && lahv.isReinflateRequired()) {
+                    LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) v;
+                    if (lahv.isReinflateRequired()) {
                         // Remove and rebind the current widget (which was inflated in the wrong
                         // orientation), but don't delete it from the database
                         mLauncher.removeItem(lahv, info, false  /* deleteFromDb */);
@@ -1224,7 +1362,7 @@
             }
         }
 
-        if (mDelayedResizeRunnable != null) {
+        if (mDelayedResizeRunnable != null && !mIsSwitchingState) {
             mDelayedResizeRunnable.run();
             mDelayedResizeRunnable = null;
         }
@@ -1298,6 +1436,32 @@
         super.scrollTo(x, y);
     }
 
+    private void onWorkspaceOverallScrollChanged() {
+        if (!mIgnoreQsbScroll) {
+            mLauncher.getQsbContainer().setTranslationX(
+                    mOverlayTranslation + mFirstPageScrollX - getScrollX());
+        }
+    }
+
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        onWorkspaceOverallScrollChanged();
+
+        // Update the page indicator progress.
+        boolean isTransitioning = mIsSwitchingState
+                || (getLayoutTransition() != null && getLayoutTransition().isRunning());
+        if (!isTransitioning) {
+            showPageIndicatorAtCurrentScroll();
+        }
+    }
+
+    private void showPageIndicatorAtCurrentScroll() {
+        if (mPageIndicator != null) {
+            mPageIndicator.setScroll(getScrollX(), computeMaxScrollX());
+        }
+    }
+
     @Override
     protected void overScroll(float amount) {
         boolean shouldOverScroll = (amount <= 0 && (!hasCustomContent() || mIsRtl)) ||
@@ -1350,19 +1514,103 @@
         // TODO(adamcohen): figure out a final effect here. We may need to recommend
         // different effects based on device performance. On at least one relatively high-end
         // device I've tried, translating the launcher causes things to get quite laggy.
-        setTranslationAndAlpha(mLauncher.getSearchDropTargetBar(), transX, alpha);
-        setTranslationAndAlpha(getPageIndicator(), transX, alpha);
-        setTranslationAndAlpha(getChildAt(getCurrentPage()), transX, alpha);
-        setTranslationAndAlpha(mLauncher.getHotseat(), transX, alpha);
+        setWorkspaceTranslationAndAlpha(Direction.X, transX, alpha);
+        setHotseatTranslationAndAlpha(Direction.X, transX, alpha);
+        onWorkspaceOverallScrollChanged();
+
+        mQsbAlphaController.setAlphaAtIndex(alpha, QSB_ALPHA_INDEX_OVERLAY_SCROLL);
+    }
+
+    /**
+     * Moves the workspace UI in the Y direction.
+     * @param translation the amount of shift.
+     * @param alpha the alpha for the workspace page
+     */
+    public void setWorkspaceYTranslationAndAlpha(float translation, float alpha) {
+        setWorkspaceTranslationAndAlpha(Direction.Y, translation, alpha);
+
+        mLauncher.getQsbContainer().setTranslationY(translation);
+        mQsbAlphaController.setAlphaAtIndex(alpha, QSB_ALPHA_INDEX_Y_TRANSLATION);
+    }
+
+    /**
+     * Moves the workspace UI in the provided direction.
+     * @param direction the direction to move the workspace
+     * @param translation the amount of shift.
+     * @param alpha the alpha for the workspace page
+     */
+    private void setWorkspaceTranslationAndAlpha(Direction direction, float translation, float alpha) {
+        Property<View, Float> property = direction.viewProperty;
+        mPageAlpha[direction.ordinal()] = alpha;
+        float finalAlpha = mPageAlpha[0] * mPageAlpha[1];
+
+        View currentChild = getChildAt(getCurrentPage());
+        if (currentChild != null) {
+            property.set(currentChild, translation);
+            currentChild.setAlpha(finalAlpha);
+        }
 
         // When the animation finishes, reset all pages, just in case we missed a page.
-        if (transX == 0) {
+        if (Float.compare(translation, 0) == 0) {
             for (int i = getChildCount() - 1; i >= 0; i--) {
-                setTranslationAndAlpha(getChildAt(i), 0, alpha);
+                View child = getChildAt(i);
+                property.set(child, translation);
+                child.setAlpha(finalAlpha);
             }
         }
     }
 
+    /**
+     * Moves the Hotseat UI in the provided direction.
+     * @param direction the direction to move the workspace
+     * @param translation the amount of shift.
+     * @param alpha the alpha for the hotseat page
+     */
+    public void setHotseatTranslationAndAlpha(Direction direction, float translation, float alpha) {
+        Property<View, Float> property = direction.viewProperty;
+        // Skip the page indicator movement in the vertical bar layout
+        if (direction != Direction.Y || !mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            property.set(mPageIndicator, translation);
+        }
+        property.set(mLauncher.getHotseat(), translation);
+        setHotseatAlphaAtIndex(alpha, direction.ordinal());
+    }
+
+    private void setHotseatAlphaAtIndex(float alpha, int index) {
+        mHotseatAlpha[index] = alpha;
+        final float hotseatAlpha = mHotseatAlpha[0] * mHotseatAlpha[1] * mHotseatAlpha[2];
+        final float pageIndicatorAlpha = mHotseatAlpha[0] * mHotseatAlpha[2];
+
+        mLauncher.getHotseat().setAlpha(hotseatAlpha);
+        mPageIndicator.setAlpha(pageIndicatorAlpha);
+    }
+
+    public ValueAnimator createHotseatAlphaAnimator(float finalValue) {
+        if (Float.compare(finalValue, mHotseatAlpha[HOTSEAT_STATE_ALPHA_INDEX]) == 0) {
+            // Return a dummy animator to avoid null checks.
+            return ValueAnimator.ofFloat(0, 0);
+        } else {
+            ValueAnimator animator = ValueAnimator
+                    .ofFloat(mHotseatAlpha[HOTSEAT_STATE_ALPHA_INDEX], finalValue);
+            animator.addUpdateListener(new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                    float value = (Float) valueAnimator.getAnimatedValue();
+                    setHotseatAlphaAtIndex(value, HOTSEAT_STATE_ALPHA_INDEX);
+                }
+            });
+
+            AccessibilityManager am = (AccessibilityManager)
+                    mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE);
+            final boolean accessibilityEnabled = am.isEnabled();
+            animator.addUpdateListener(
+                    new AlphaUpdateListener(mLauncher.getHotseat(), accessibilityEnabled));
+            animator.addUpdateListener(
+                    new AlphaUpdateListener(mPageIndicator, accessibilityEnabled));
+            return animator;
+        }
+    }
+
     @Override
     protected Matrix getPageShiftMatrix() {
         if (Float.compare(mOverlayTranslation, 0) != 0) {
@@ -1375,13 +1623,6 @@
         return super.getPageShiftMatrix();
     }
 
-    private void setTranslationAndAlpha(View v, float transX, float alpha) {
-        if (v != null) {
-            v.setTranslationX(transX);
-            v.setAlpha(alpha);
-        }
-    }
-
     @Override
     protected void getEdgeVerticalPostion(int[] pos) {
         View child = getChildAt(getPageCount() - 1);
@@ -1412,17 +1653,28 @@
     }
 
     protected void setWallpaperDimension() {
-        new AsyncTask<Void, Void, Void>() {
-            public Void doInBackground(Void ... args) {
-                String spKey = LauncherFiles.WALLPAPER_CROP_PREFERENCES_KEY;
-                SharedPreferences sp =
-                        mLauncher.getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
-                WallpaperUtils.suggestWallpaperDimension(mLauncher.getResources(),
-                        sp, mLauncher.getWindowManager(), mWallpaperManager,
-                        mLauncher.overrideWallpaperDimensions());
-                return null;
+        Utilities.THREAD_POOL_EXECUTOR.execute(new Runnable() {
+            @Override
+            public void run() {
+                final Point size = LauncherAppState.getInstance()
+                        .getInvariantDeviceProfile().defaultWallpaperSize;
+                if (size.x != mWallpaperManager.getDesiredMinimumWidth()
+                        || size.y != mWallpaperManager.getDesiredMinimumHeight()) {
+                    mWallpaperManager.suggestDesiredDimensions(size.x, size.y);
+                }
             }
-        }.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
+        });
+    }
+
+    public void lockWallpaperToDefaultPage() {
+        mWallpaperOffset.setLockToDefaultPage(true);
+    }
+
+    public void unlockWallpaperFromDefaultPageOnNextLayout() {
+        if (mWallpaperOffset.isLockedToDefaultPage()) {
+            mUnlockWallpaperFromDefaultPageOnLayout = true;
+            requestLayout();
+        }
     }
 
     protected void snapToPage(int whichPage, Runnable r) {
@@ -1445,197 +1697,16 @@
         snapToPage(getPageIndexForScreenId(screenId), r);
     }
 
-    class WallpaperOffsetInterpolator implements Choreographer.FrameCallback {
-        float mFinalOffset = 0.0f;
-        float mCurrentOffset = 0.5f; // to force an initial update
-        boolean mWaitingForUpdate;
-        Choreographer mChoreographer;
-        Interpolator mInterpolator;
-        boolean mAnimating;
-        long mAnimationStartTime;
-        float mAnimationStartOffset;
-        private final int ANIMATION_DURATION = 250;
-        // Don't use all the wallpaper for parallax until you have at least this many pages
-        private final int MIN_PARALLAX_PAGE_SPAN = 3;
-        int mNumScreens;
-
-        public WallpaperOffsetInterpolator() {
-            mChoreographer = Choreographer.getInstance();
-            mInterpolator = new DecelerateInterpolator(1.5f);
-        }
-
-        @Override
-        public void doFrame(long frameTimeNanos) {
-            updateOffset(false);
-        }
-
-        private void updateOffset(boolean force) {
-            if (mWaitingForUpdate || force) {
-                mWaitingForUpdate = false;
-                if (computeScrollOffset() && mWindowToken != null) {
-                    try {
-                        mWallpaperManager.setWallpaperOffsets(mWindowToken,
-                                mWallpaperOffset.getCurrX(), 0.5f);
-                        setWallpaperOffsetSteps();
-                    } catch (IllegalArgumentException e) {
-                        Log.e(TAG, "Error updating wallpaper offset: " + e);
-                    }
-                }
-            }
-        }
-
-        public boolean computeScrollOffset() {
-            final float oldOffset = mCurrentOffset;
-            if (mAnimating) {
-                long durationSinceAnimation = System.currentTimeMillis() - mAnimationStartTime;
-                float t0 = durationSinceAnimation / (float) ANIMATION_DURATION;
-                float t1 = mInterpolator.getInterpolation(t0);
-                mCurrentOffset = mAnimationStartOffset +
-                        (mFinalOffset - mAnimationStartOffset) * t1;
-                mAnimating = durationSinceAnimation < ANIMATION_DURATION;
-            } else {
-                mCurrentOffset = mFinalOffset;
-            }
-
-            if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) {
-                scheduleUpdate();
-            }
-            if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) {
-                return true;
-            }
-            return false;
-        }
-
-        public float wallpaperOffsetForScroll(int scroll) {
-            // TODO: do different behavior if it's  a live wallpaper?
-            // Don't use up all the wallpaper parallax until you have at least
-            // MIN_PARALLAX_PAGE_SPAN pages
-            int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
-            int parallaxPageSpan;
-            if (mWallpaperIsLiveWallpaper) {
-                parallaxPageSpan = numScrollingPages - 1;
-            } else {
-                parallaxPageSpan = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages - 1);
-            }
-            mNumPagesForWallpaperParallax = parallaxPageSpan;
-
-            if (getChildCount() <= 1) {
-                if (mIsRtl) {
-                    return 1 - 1.0f/mNumPagesForWallpaperParallax;
-                }
-                return 0;
-            }
-
-            // Exclude the leftmost page
-            int emptyExtraPages = numEmptyScreensToIgnore();
-            int firstIndex = numCustomPages();
-            // Exclude the last extra empty screen (if we have > MIN_PARALLAX_PAGE_SPAN pages)
-            int lastIndex = getChildCount() - 1 - emptyExtraPages;
-            if (mIsRtl) {
-                int temp = firstIndex;
-                firstIndex = lastIndex;
-                lastIndex = temp;
-            }
-
-            int firstPageScrollX = getScrollForPage(firstIndex);
-            int scrollRange = getScrollForPage(lastIndex) - firstPageScrollX;
-            if (scrollRange == 0) {
-                return 0;
-            } else {
-                // Sometimes the left parameter of the pages is animated during a layout transition;
-                // this parameter offsets it to keep the wallpaper from animating as well
-                int adjustedScroll =
-                        scroll - firstPageScrollX - getLayoutTransitionOffsetForPage(0);
-                float offset = Math.min(1, adjustedScroll / (float) scrollRange);
-                offset = Math.max(0, offset);
-
-                // On RTL devices, push the wallpaper offset to the right if we don't have enough
-                // pages (ie if numScrollingPages < MIN_PARALLAX_PAGE_SPAN)
-                if (!mWallpaperIsLiveWallpaper && numScrollingPages < MIN_PARALLAX_PAGE_SPAN
-                        && mIsRtl) {
-                    return offset * (parallaxPageSpan - numScrollingPages + 1) / parallaxPageSpan;
-                }
-                return offset * (numScrollingPages - 1) / parallaxPageSpan;
-            }
-        }
-
-        private float wallpaperOffsetForCurrentScroll() {
-            return wallpaperOffsetForScroll(getScrollX());
-        }
-
-        private int numEmptyScreensToIgnore() {
-            int numScrollingPages = getChildCount() - numCustomPages();
-            if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && hasExtraEmptyScreen()) {
-                return 1;
-            } else {
-                return 0;
-            }
-        }
-
-        private int getNumScreensExcludingEmptyAndCustom() {
-            int numScrollingPages = getChildCount() - numEmptyScreensToIgnore() - numCustomPages();
-            return numScrollingPages;
-        }
-
-        public void syncWithScroll() {
-            float offset = wallpaperOffsetForCurrentScroll();
-            mWallpaperOffset.setFinalX(offset);
-            updateOffset(true);
-        }
-
-        public float getCurrX() {
-            return mCurrentOffset;
-        }
-
-        public float getFinalX() {
-            return mFinalOffset;
-        }
-
-        private void animateToFinal() {
-            mAnimating = true;
-            mAnimationStartOffset = mCurrentOffset;
-            mAnimationStartTime = System.currentTimeMillis();
-        }
-
-        private void setWallpaperOffsetSteps() {
-            // Set wallpaper offset steps (1 / (number of screens - 1))
-            float xOffset = 1.0f / mNumPagesForWallpaperParallax;
-            if (xOffset != mLastSetWallpaperOffsetSteps) {
-                mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f);
-                mLastSetWallpaperOffsetSteps = xOffset;
-            }
-        }
-
-        public void setFinalX(float x) {
-            scheduleUpdate();
-            mFinalOffset = Math.max(0f, Math.min(x, 1.0f));
-            if (getNumScreensExcludingEmptyAndCustom() != mNumScreens) {
-                if (mNumScreens > 0) {
-                    // Don't animate if we're going from 0 screens
-                    animateToFinal();
-                }
-                mNumScreens = getNumScreensExcludingEmptyAndCustom();
-            }
-        }
-
-        private void scheduleUpdate() {
-            if (!mWaitingForUpdate) {
-                mChoreographer.postFrameCallback(this);
-                mWaitingForUpdate = true;
-            }
-        }
-
-        public void jumpToFinal() {
-            mCurrentOffset = mFinalOffset;
-        }
-    }
-
     @Override
     public void computeScroll() {
         super.computeScroll();
         mWallpaperOffset.syncWithScroll();
     }
 
+    public void computeScrollWithoutInvalidation() {
+        computeScrollHelper(false);
+    }
+
     @Override
     protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
         if (!isSwitchingState()) {
@@ -1657,18 +1728,6 @@
         }
     }
 
-    float backgroundAlphaInterpolator(float r) {
-        float pivotA = 0.1f;
-        float pivotB = 0.4f;
-        if (r < pivotA) {
-            return 0;
-        } else if (r > pivotB) {
-            return 1.0f;
-        } else {
-            return (r - pivotA)/(pivotB - pivotA);
-        }
-    }
-
     private void updatePageAlphaValues(int screenCenter) {
         if (mWorkspaceFadeInAdjacentScreens &&
                 !workspaceInModalState() &&
@@ -1679,31 +1738,15 @@
                     float scrollProgress = getScrollProgress(screenCenter, child, i);
                     float alpha = 1 - Math.abs(scrollProgress);
                     child.getShortcutsAndWidgets().setAlpha(alpha);
+
+                    if (isQsbContainerPage(i)) {
+                        mQsbAlphaController.setAlphaAtIndex(alpha, QSB_ALPHA_INDEX_PAGE_SCROLL);
+                    }
                 }
             }
         }
     }
 
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-    @Override
-    public void enableAccessibleDrag(boolean enable) {
-        for (int i = 0; i < getChildCount(); i++) {
-            CellLayout child = (CellLayout) getChildAt(i);
-            child.enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
-        }
-
-        if (enable) {
-            // We need to allow our individual children to become click handlers in this case
-            setOnClickListener(null);
-        } else {
-            // Reset our click listener
-            setOnClickListener(mLauncher);
-        }
-        mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable);
-        mLauncher.getHotseat().getLayout()
-            .enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
-    }
-
     public boolean hasCustomContent() {
         return (mScreenOrder.size() > 0 && mScreenOrder.get(0) == CUSTOM_CONTENT_SCREEN_ID);
     }
@@ -1755,8 +1798,8 @@
             mLauncher.getHotseat().setTranslationX(translationX);
         }
 
-        if (getPageIndicator() != null) {
-            getPageIndicator().setTranslationX(translationX);
+        if (mPageIndicator != null) {
+            mPageIndicator.setTranslationX(translationX);
         }
 
         if (mCustomContentCallbacks != null) {
@@ -1765,22 +1808,6 @@
     }
 
     @Override
-    protected OnClickListener getPageIndicatorClickListener() {
-        AccessibilityManager am = (AccessibilityManager)
-                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-        if (!am.isTouchExplorationEnabled()) {
-            return null;
-        }
-        OnClickListener listener = new OnClickListener() {
-            @Override
-            public void onClick(View arg0) {
-                mLauncher.showOverviewMode(true);
-            }
-        };
-        return listener;
-    }
-
-    @Override
     protected void screenScrolled(int screenCenter) {
         updatePageAlphaValues(screenCenter);
         updateStateForCustomContent(screenCenter);
@@ -1789,65 +1816,66 @@
 
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        mWindowToken = getWindowToken();
+        IBinder windowToken = getWindowToken();
+        mWallpaperOffset.setWindowToken(windowToken);
         computeScroll();
-        mDragController.setWindowToken(mWindowToken);
+        mDragController.setWindowToken(windowToken);
     }
 
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        mWindowToken = null;
+        mWallpaperOffset.setWindowToken(null);
     }
 
     protected void onResume() {
-        if (getPageIndicator() != null) {
-            // In case accessibility state has changed, we need to perform this on every
-            // attach to window
-            OnClickListener listener = getPageIndicatorClickListener();
-            if (listener != null) {
-                getPageIndicator().setOnClickListener(listener);
-            }
-        }
-
         // Update wallpaper dimensions if they were changed since last onResume
         // (we also always set the wallpaper dimensions in the constructor)
         if (LauncherAppState.getInstance().hasWallpaperChangedSinceLastCheck()) {
             setWallpaperDimension();
         }
-        mWallpaperIsLiveWallpaper = mWallpaperManager.getWallpaperInfo() != null;
-        // Force the wallpaper offset steps to be set again, because another app might have changed
-        // them
-        mLastSetWallpaperOffsetSteps = 0f;
+        mWallpaperOffset.onResume();
     }
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        if (mUnlockWallpaperFromDefaultPageOnLayout) {
+            mWallpaperOffset.setLockToDefaultPage(false);
+            mUnlockWallpaperFromDefaultPageOnLayout = false;
+        }
         if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
             mWallpaperOffset.syncWithScroll();
             mWallpaperOffset.jumpToFinal();
         }
         super.onLayout(changed, left, top, right, bottom);
-    }
+        mFirstPageScrollX = getScrollForPage(0);
+        onWorkspaceOverallScrollChanged();
 
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
+        final LayoutTransition transition = getLayoutTransition();
+        // If the transition is running defer updating max scroll, as some empty pages could
+        // still be present, and a max scroll change could cause sudden jumps in scroll.
+        if (transition != null && transition.isRunning()) {
+            transition.addTransitionListener(new LayoutTransition.TransitionListener() {
 
-        // Call back to LauncherModel to finish binding after the first draw
-        post(mBindPages);
-    }
+                @Override
+                public void startTransition(LayoutTransition transition, ViewGroup container,
+                                            View view, int transitionType) {
+                    mIgnoreQsbScroll = true;
+                }
 
-    @Override
-    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
-        if (!mLauncher.isAppsViewVisible()) {
-            final Folder openFolder = getOpenFolder();
-            if (openFolder != null) {
-                return openFolder.requestFocus(direction, previouslyFocusedRect);
-            } else {
-                return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
-            }
+                @Override
+                public void endTransition(LayoutTransition transition, ViewGroup container,
+                                          View view, int transitionType) {
+                    // Wait until all transitions are complete.
+                    if (!transition.isRunning()) {
+                        mIgnoreQsbScroll = false;
+                        transition.removeTransitionListener(this);
+                        mFirstPageScrollX = getScrollForPage(0);
+                        onWorkspaceOverallScrollChanged();
+                    }
+                }
+            });
         }
-        return false;
+
     }
 
     @Override
@@ -1858,18 +1886,6 @@
         return super.getDescendantFocusability();
     }
 
-    @Override
-    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
-        if (!mLauncher.isAppsViewVisible()) {
-            final Folder openFolder = getOpenFolder();
-            if (openFolder != null) {
-                openFolder.addFocusables(views, direction);
-            } else {
-                super.addFocusables(views, direction, focusableMode);
-            }
-        }
-    }
-
     public boolean workspaceInModalState() {
         return mState != State.NORMAL;
     }
@@ -1963,8 +1979,18 @@
         updateChildrenLayersEnabled(false);
     }
 
+    @Override
+    protected void getVisiblePages(int[] range) {
+        super.getVisiblePages(range);
+        if (mForceDrawAdjacentPages) {
+            // In overview mode, make sure that the two side pages are visible.
+            range[0] = Utilities.boundToRange(getCurrentPage() - 1, numCustomPages(), range[1]);
+            range[1] = Utilities.boundToRange(getCurrentPage() + 1, range[0], getPageCount() - 1);
+        }
+    }
+
     protected void onWallpaperTap(MotionEvent ev) {
-        final int[] position = mTempCell;
+        final int[] position = mTempXY;
         getLocationOnScreen(position);
 
         int pointerIndex = ev.getActionIndex();
@@ -1977,61 +2003,8 @@
                 position[0], position[1], 0, null);
     }
 
-    /*
-    *
-    * We call these methods (onDragStartedWithItemSpans/onDragStartedWithSize) whenever we
-    * start a drag in Launcher, regardless of whether the drag has ever entered the Workspace
-    *
-    * These methods mark the appropriate pages as accepting drops (which alters their visual
-    * appearance).
-    *
-    */
-    private static Rect getDrawableBounds(Drawable d) {
-        Rect bounds = new Rect();
-        d.copyBounds(bounds);
-        if (bounds.width() == 0 || bounds.height() == 0) {
-            bounds.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
-        } else {
-            bounds.offsetTo(0, 0);
-        }
-        if (d instanceof PreloadIconDrawable) {
-            int inset = -((PreloadIconDrawable) d).getOutset();
-            bounds.inset(inset, inset);
-        }
-        return bounds;
-    }
-
-    public void onExternalDragStartedWithItem(View v) {
-        // Compose a drag bitmap with the view scaled to the icon size
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        int iconSize = grid.iconSizePx;
-        int bmpWidth = v.getMeasuredWidth();
-        int bmpHeight = v.getMeasuredHeight();
-
-        // If this is a text view, use its drawable instead
-        if (v instanceof TextView) {
-            Drawable d = getTextViewIcon((TextView) v);
-            Rect bounds = getDrawableBounds(d);
-            bmpWidth = bounds.width();
-            bmpHeight = bounds.height();
-        }
-
-        // Compose the bitmap to create the icon from
-        Bitmap b = Bitmap.createBitmap(bmpWidth, bmpHeight,
-                Bitmap.Config.ARGB_8888);
-        mCanvas.setBitmap(b);
-        drawDragView(v, mCanvas, 0);
-        mCanvas.setBitmap(null);
-
-        // The outline is used to visualize where the item will land if dropped
-        mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, iconSize, iconSize, true);
-    }
-
-    public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
-        int[] size = estimateItemSize(info, false);
-
-        // The outline is used to visualize where the item will land if dropped
-        mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, size[0], size[1], clipAlpha);
+    public void prepareDragWithProvider(DragPreviewProvider outlineProvider) {
+        mOutlineProvider = outlineProvider;
     }
 
     public void exitWidgetResizeMode() {
@@ -2049,7 +2022,7 @@
         int end = getChildCount() - 1;
 
         range[0] = Math.max(0, Math.min(start, getChildCount() - 1));
-        range[1] = Math.max(0,  end);
+        range[1] = Math.max(0, end);
     }
 
     public void onStartReordering() {
@@ -2083,12 +2056,16 @@
         return mState == State.OVERVIEW;
     }
 
+    public void snapToPageFromOverView(int whichPage) {
+        mStateTransitionAnimation.snapToPageFromOverView(whichPage);
+    }
+
     int getOverviewModeTranslationY() {
         DeviceProfile grid = mLauncher.getDeviceProfile();
-        Rect workspacePadding = grid.getWorkspacePadding(Utilities.isRtl(getResources()));
         int overviewButtonBarHeight = grid.getOverviewModeButtonBarHeight();
 
         int scaledHeight = (int) (mOverviewModeShrinkFactor * getNormalChildHeight());
+        Rect workspacePadding = grid.getWorkspacePadding(sTempRect);
         int workspaceTop = mInsets.top + workspacePadding.top;
         int workspaceBottom = getViewportHeight() - mInsets.bottom - workspacePadding.bottom;
         int overviewTop = mInsets.top;
@@ -2098,15 +2075,41 @@
         return -workspaceOffsetTopEdge + overviewOffsetTopEdge;
     }
 
+    float getSpringLoadedTranslationY() {
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        if (grid.isVerticalBarLayout() || getChildCount() == 0) {
+            return 0;
+        }
+
+        float scaledHeight = grid.workspaceSpringLoadShrinkFactor * getNormalChildHeight();
+        float shrunkTop = mInsets.top + grid.dropTargetBarSizePx;
+        float shrunkBottom = getViewportHeight() - mInsets.bottom
+                - grid.getWorkspacePadding(sTempRect).bottom
+                - grid.workspaceSpringLoadedBottomSpace;
+        float totalShrunkSpace = shrunkBottom - shrunkTop;
+
+        float desiredCellTop = shrunkTop + (totalShrunkSpace - scaledHeight) / 2;
+
+        float halfHeight = getHeight() / 2;
+        float myCenter = getTop() + halfHeight;
+        float cellTopFromCenter = halfHeight - getChildAt(0).getTop();
+        float actualCellTop = myCenter - cellTopFromCenter * grid.workspaceSpringLoadShrinkFactor;
+        return (desiredCellTop - actualCellTop) / grid.workspaceSpringLoadShrinkFactor;
+    }
+
+    float getOverviewModeShrinkFactor() {
+        return mOverviewModeShrinkFactor;
+    }
+
     /**
      * Sets the current workspace {@link State}, returning an animation transitioning the workspace
      * to that new state.
      */
-    public Animator setStateWithAnimation(State toState, int toPage, boolean animated,
+    public Animator setStateWithAnimation(State toState, boolean animated,
             HashMap<View, Integer> layerViews) {
         // Create the animation to the new state
-        Animator workspaceAnim =  mStateTransitionAnimation.getAnimationToState(mState,
-                toState, toPage, animated, layerViews);
+        AnimatorSet workspaceAnim =  mStateTransitionAnimation.getAnimationToState(mState,
+                toState, animated, layerViews);
 
         boolean shouldNotifyWidgetChange = !mState.shouldUpdateWidget
                 && toState.shouldUpdateWidget;
@@ -2118,27 +2121,34 @@
             mLauncher.notifyWidgetProvidersChanged();
         }
 
+        if (mOnStateChangeListener != null) {
+            mOnStateChangeListener.prepareStateChange(toState, animated ? workspaceAnim : null);
+        }
+
         return workspaceAnim;
     }
 
-    State getState() {
+    public State getState() {
         return mState;
     }
 
     public void updateAccessibilityFlags() {
-        if (Utilities.ATLEAST_LOLLIPOP) {
-            int total = getPageCount();
-            for (int i = numCustomPages(); i < total; i++) {
-                updateAccessibilityFlags((CellLayout) getPageAt(i), i);
-            }
-            setImportantForAccessibility((mState == State.NORMAL || mState == State.OVERVIEW)
-                    ? IMPORTANT_FOR_ACCESSIBILITY_AUTO
-                    : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
-        } else {
-            int accessible = mState == State.NORMAL ?
-                    IMPORTANT_FOR_ACCESSIBILITY_AUTO :
+        // TODO: Update the accessibility flags appropriately when dragging.
+        if (!mLauncher.getAccessibilityDelegate().isInAccessibleDrag()) {
+            if (Utilities.ATLEAST_LOLLIPOP) {
+                int total = getPageCount();
+                for (int i = numCustomPages(); i < total; i++) {
+                    updateAccessibilityFlags((CellLayout) getPageAt(i), i);
+                }
+                setImportantForAccessibility((mState == State.NORMAL || mState == State.OVERVIEW)
+                        ? IMPORTANT_FOR_ACCESSIBILITY_AUTO
+                        : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+            } else {
+                int accessible = mState == State.NORMAL ?
+                        IMPORTANT_FOR_ACCESSIBILITY_AUTO :
                         IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
-            setImportantForAccessibility(accessible);
+                setImportantForAccessibility(accessible);
+            }
         }
     }
 
@@ -2149,10 +2159,13 @@
                     IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
             page.setContentDescription(getPageDescription(pageNo));
 
-            if (mPagesAccessibilityDelegate == null) {
-                mPagesAccessibilityDelegate = new OverviewScreenAccessibilityDelegate(this);
+            // No custom action for the first page.
+            if (!FeatureFlags.QSB_ON_FIRST_SCREEN || pageNo > 0) {
+                if (mPagesAccessibilityDelegate == null) {
+                    mPagesAccessibilityDelegate = new OverviewScreenAccessibilityDelegate(this);
+                }
+                page.setAccessibilityDelegate(mPagesAccessibilityDelegate);
             }
-            page.setAccessibilityDelegate(mPagesAccessibilityDelegate);
         } else {
             int accessible = mState == State.NORMAL ?
                     IMPORTANT_FOR_ACCESSIBILITY_AUTO :
@@ -2165,12 +2178,15 @@
     }
 
     @Override
-    public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
+    public void onLauncherTransitionPrepare(Launcher l, boolean animated,
+            boolean multiplePagesVisible) {
         mIsSwitchingState = true;
         mTransitionProgress = 0;
 
-        // Invalidate here to ensure that the pages are rendered during the state change transition.
-        invalidate();
+        if (multiplePagesVisible) {
+            mForceDrawAdjacentPages = true;
+        }
+        invalidate(); // This will call dispatchDraw(), which calls getVisiblePages().
 
         updateChildrenLayersEnabled(false);
         hideCustomContentIfNecessary();
@@ -2178,6 +2194,14 @@
 
     @Override
     public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
+        if (mPageIndicator != null) {
+            boolean isNewStateSpringLoaded = mState == State.SPRING_LOADED;
+            mPageIndicator.setShouldAutoHide(!isNewStateSpringLoaded);
+            if (isNewStateSpringLoaded) {
+                // Show the page indicator at the same time as the rest of the transition.
+                showPageIndicatorAtCurrentScroll();
+            }
+        }
     }
 
     @Override
@@ -2190,10 +2214,18 @@
         mIsSwitchingState = false;
         updateChildrenLayersEnabled(false);
         showCustomContentIfNecessary();
+        mForceDrawAdjacentPages = false;
+        if (mState == State.SPRING_LOADED) {
+            showPageIndicatorAtCurrentScroll();
+        }
     }
 
     void updateCustomContentVisibility() {
         int visibility = mState == Workspace.State.NORMAL ? VISIBLE : INVISIBLE;
+        setCustomContentVisibility(visibility);
+    }
+
+    void setCustomContentVisibility(int visibility) {
         if (hasCustomContent()) {
             mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID).setVisibility(visibility);
         }
@@ -2228,125 +2260,7 @@
         return null;
     }
 
-    /**
-     * Draw the View v into the given Canvas.
-     *
-     * @param v the view to draw
-     * @param destCanvas the canvas to draw on
-     * @param padding the horizontal and vertical padding to use when drawing
-     */
-    private static void drawDragView(View v, Canvas destCanvas, int padding) {
-        final Rect clipRect = sTempRect;
-        v.getDrawingRect(clipRect);
-
-        boolean textVisible = false;
-
-        destCanvas.save();
-        if (v instanceof TextView) {
-            Drawable d = getTextViewIcon((TextView) v);
-            Rect bounds = getDrawableBounds(d);
-            clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding);
-            destCanvas.translate(padding / 2 - bounds.left, padding / 2 - bounds.top);
-            d.draw(destCanvas);
-        } else {
-            if (v instanceof FolderIcon) {
-                // For FolderIcons the text can bleed into the icon area, and so we need to
-                // hide the text completely (which can't be achieved by clipping).
-                if (((FolderIcon) v).getTextVisible()) {
-                    ((FolderIcon) v).setTextVisible(false);
-                    textVisible = true;
-                }
-            }
-            destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
-            destCanvas.clipRect(clipRect, Op.REPLACE);
-            v.draw(destCanvas);
-
-            // Restore text visibility of FolderIcon if necessary
-            if (textVisible) {
-                ((FolderIcon) v).setTextVisible(true);
-            }
-        }
-        destCanvas.restore();
-    }
-
-    /**
-     * Returns a new bitmap to show when the given View is being dragged around.
-     * Responsibility for the bitmap is transferred to the caller.
-     * @param expectedPadding padding to add to the drag view. If a different padding was used
-     * its value will be changed
-     */
-    public Bitmap createDragBitmap(View v, AtomicInteger expectedPadding) {
-        Bitmap b;
-
-        int padding = expectedPadding.get();
-        if (v instanceof TextView) {
-            Drawable d = getTextViewIcon((TextView) v);
-            Rect bounds = getDrawableBounds(d);
-            b = Bitmap.createBitmap(bounds.width() + padding,
-                    bounds.height() + padding, Bitmap.Config.ARGB_8888);
-            expectedPadding.set(padding - bounds.left - bounds.top);
-        } else {
-            b = Bitmap.createBitmap(
-                    v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
-        }
-
-        mCanvas.setBitmap(b);
-        drawDragView(v, mCanvas, padding);
-        mCanvas.setBitmap(null);
-
-        return b;
-    }
-
-    /**
-     * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
-     * Responsibility for the bitmap is transferred to the caller.
-     */
-    private Bitmap createDragOutline(View v, int padding) {
-        final int outlineColor = getResources().getColor(R.color.outline_color);
-        final Bitmap b = Bitmap.createBitmap(
-                v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
-
-        mCanvas.setBitmap(b);
-        drawDragView(v, mCanvas, padding);
-        mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor);
-        mCanvas.setBitmap(null);
-        return b;
-    }
-
-    /**
-     * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
-     * Responsibility for the bitmap is transferred to the caller.
-     */
-    private Bitmap createDragOutline(Bitmap orig, int padding, int w, int h,
-            boolean clipAlpha) {
-        final int outlineColor = getResources().getColor(R.color.outline_color);
-        final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
-        mCanvas.setBitmap(b);
-
-        Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
-        float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
-                (h - padding) / (float) orig.getHeight());
-        int scaledWidth = (int) (scaleFactor * orig.getWidth());
-        int scaledHeight = (int) (scaleFactor * orig.getHeight());
-        Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
-
-        // center the image
-        dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
-
-        mCanvas.drawBitmap(orig, src, dst, null);
-        mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor,
-                clipAlpha);
-        mCanvas.setBitmap(null);
-
-        return b;
-    }
-
-    public void startDrag(CellLayout.CellInfo cellInfo) {
-        startDrag(cellInfo, false);
-    }
-
-    @Override
-    public void startDrag(CellLayout.CellInfo cellInfo, boolean accessible) {
+    public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) {
         View child = cellInfo.cell;
 
         // Make sure the drag was started by a long press as opposed to a long click.
@@ -2359,64 +2273,68 @@
         CellLayout layout = (CellLayout) child.getParent().getParent();
         layout.prepareChildForDrag(child);
 
-        beginDragShared(child, this, accessible);
+        if (options.isAccessibleDrag) {
+            mDragController.addDragListener(new AccessibileDragListenerAdapter(
+                    this, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG) {
+                @Override
+                protected void enableAccessibleDrag(boolean enable) {
+                    super.enableAccessibleDrag(enable);
+                    setEnableForLayout(mLauncher.getHotseat().getLayout(),enable);
+
+                    // We need to allow our individual children to become click handlers in this
+                    // case, so temporarily unset the click handlers.
+                    setOnClickListener(enable ? null : mLauncher);
+                }
+            });
+        }
+
+        beginDragShared(child, this, options);
     }
 
-    public void beginDragShared(View child, DragSource source, boolean accessible) {
-        beginDragShared(child, new Point(), source, accessible);
+    public void beginDragShared(View child, DragSource source, DragOptions options) {
+        Object dragObject = child.getTag();
+        if (!(dragObject instanceof ItemInfo)) {
+            String msg = "Drag started with a view that has no tag set. This "
+                    + "will cause a crash (issue 11627249) down the line. "
+                    + "View: " + child + "  tag: " + child.getTag();
+            throw new IllegalStateException(msg);
+        }
+        beginDragShared(child, source, (ItemInfo) dragObject,
+                new DragPreviewProvider(child), options);
     }
 
-    public void beginDragShared(View child, Point relativeTouchPos, DragSource source,
-            boolean accessible) {
+
+    public DragView beginDragShared(View child, DragSource source, ItemInfo dragObject,
+            DragPreviewProvider previewProvider, DragOptions dragOptions) {
         child.clearFocus();
         child.setPressed(false);
+        mOutlineProvider = previewProvider;
 
-        // The outline is used to visualize where the item will land if dropped
-        mDragOutline = createDragOutline(child, DRAG_BITMAP_PADDING);
-
-        mLauncher.onDragStarted(child);
         // The drag bitmap follows the touch point around on the screen
-        AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
-        final Bitmap b = createDragBitmap(child, padding);
+        final Bitmap b = previewProvider.createDragBitmap(mCanvas);
+        int halfPadding = previewProvider.previewPadding / 2;
 
-        final int bmpWidth = b.getWidth();
-        final int bmpHeight = b.getHeight();
-
-        float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
-        int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
-        int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2
-                        - padding.get() / 2);
+        float scale = previewProvider.getScaleAndPosition(b, mTempXY);
+        int dragLayerX = mTempXY[0];
+        int dragLayerY = mTempXY[1];
 
         DeviceProfile grid = mLauncher.getDeviceProfile();
         Point dragVisualizeOffset = null;
         Rect dragRect = null;
         if (child instanceof BubbleTextView) {
-            BubbleTextView icon = (BubbleTextView) child;
             int iconSize = grid.iconSizePx;
             int top = child.getPaddingTop();
-            int left = (bmpWidth - iconSize) / 2;
+            int left = (b.getWidth() - iconSize) / 2;
             int right = left + iconSize;
             int bottom = top + iconSize;
-            if (icon.isLayoutHorizontal()) {
-                // If the layout is horizontal, then if we are just picking up the icon, then just
-                // use the child position since the icon is top-left aligned.  Otherwise, offset
-                // the drag layer position horizontally so that the icon is under the current
-                // touch position.
-                if (icon.getIcon().getBounds().contains(relativeTouchPos.x, relativeTouchPos.y)) {
-                    dragLayerX = Math.round(mTempXY[0]);
-                } else {
-                    dragLayerX = Math.round(mTempXY[0] + relativeTouchPos.x - (bmpWidth / 2));
-                }
-            }
             dragLayerY += top;
             // Note: The drag region is used to calculate drag layer offsets, but the
             // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
-            dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2);
+            dragVisualizeOffset = new Point(- halfPadding, halfPadding);
             dragRect = new Rect(left, top, right, bottom);
         } else if (child instanceof FolderIcon) {
             int previewSize = grid.folderIconSizePx;
-            dragVisualizeOffset = new Point(-padding.get() / 2,
-                    padding.get() / 2 - child.getPaddingTop());
+            dragVisualizeOffset = new Point(- halfPadding, halfPadding - child.getPaddingTop());
             dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize);
         }
 
@@ -2426,68 +2344,15 @@
             icon.clearPressedBackground();
         }
 
-        if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
-            String msg = "Drag started with a view that has no tag set. This "
-                    + "will cause a crash (issue 11627249) down the line. "
-                    + "View: " + child + "  tag: " + child.getTag();
-            throw new IllegalStateException(msg);
-        }
-
         if (child.getParent() instanceof ShortcutAndWidgetContainer) {
             mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
         }
 
-        DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
-                DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, accessible);
+        DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source,
+                dragObject, dragVisualizeOffset, dragRect, scale, dragOptions);
         dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
-
         b.recycle();
-    }
-
-    public void beginExternalDragShared(View child, DragSource source) {
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        int iconSize = grid.iconSizePx;
-
-        // Notify launcher of drag start
-        mLauncher.onDragStarted(child);
-
-        // Compose a new drag bitmap that is of the icon size
-        AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
-        final Bitmap tmpB = createDragBitmap(child, padding);
-        Bitmap b = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
-        Paint p = new Paint();
-        p.setFilterBitmap(true);
-        mCanvas.setBitmap(b);
-        mCanvas.drawBitmap(tmpB, new Rect(0, 0, tmpB.getWidth(), tmpB.getHeight()),
-                new Rect(0, 0, iconSize, iconSize), p);
-        mCanvas.setBitmap(null);
-
-        // Find the child's location on the screen
-        int bmpWidth = tmpB.getWidth();
-        float iconScale = (float) bmpWidth / iconSize;
-        float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY) * iconScale;
-        int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
-        int dragLayerY = Math.round(mTempXY[1]);
-
-        // Note: The drag region is used to calculate drag layer offsets, but the
-        // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
-        Point dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2);
-        Rect dragRect = new Rect(0, 0, iconSize, iconSize);
-
-        if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
-            String msg = "Drag started with a view that has no tag set. This "
-                    + "will cause a crash (issue 11627249) down the line. "
-                    + "View: " + child + "  tag: " + child.getTag();
-            throw new IllegalStateException(msg);
-        }
-
-        // Start the drag
-        DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
-                DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, false);
-        dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
-
-        // Recycle temporary bitmaps
-        tmpB.recycle();
+        return dv;
     }
 
     public boolean transitionStateShouldAllowDrop() {
@@ -2514,7 +2379,7 @@
             if (mLauncher.isHotseatLayout(dropTargetLayout)) {
                 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
             } else {
-                mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
+                mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter);
             }
 
             int spanX = 1;
@@ -2524,9 +2389,8 @@
                 spanX = dragCellInfo.spanX;
                 spanY = dragCellInfo.spanY;
             } else {
-                final ItemInfo dragInfo = (ItemInfo) d.dragInfo;
-                spanX = dragInfo.spanX;
-                spanY = dragInfo.spanY;
+                spanX = d.dragInfo.spanX;
+                spanY = d.dragInfo.spanY;
             }
 
             int minSpanX = spanX;
@@ -2541,12 +2405,12 @@
                     mTargetCell);
             float distance = dropTargetLayout.getDistanceFromCell(mDragViewVisualCenter[0],
                     mDragViewVisualCenter[1], mTargetCell);
-            if (mCreateUserFolderOnDrop && willCreateUserFolder((ItemInfo) d.dragInfo,
+            if (mCreateUserFolderOnDrop && willCreateUserFolder(d.dragInfo,
                     dropTargetLayout, mTargetCell, distance, true)) {
                 return true;
             }
 
-            if (mAddToExistingFolderOnDrop && willAddToExistingUserFolder((ItemInfo) d.dragInfo,
+            if (mAddToExistingFolderOnDrop && willAddToExistingUserFolder(d.dragInfo,
                     dropTargetLayout, mTargetCell, distance)) {
                 return true;
             }
@@ -2562,9 +2426,9 @@
                 // Don't show the message if we are dropping on the AllApps button and the hotseat
                 // is full
                 boolean isHotseat = mLauncher.isHotseatLayout(dropTargetLayout);
-                if (mTargetCell != null && isHotseat) {
+                if (mTargetCell != null && isHotseat && !FeatureFlags.NO_ALL_APPS_ICON) {
                     Hotseat hotseat = mLauncher.getHotseat();
-                    if (hotseat.isAllAppsButtonRank(
+                    if (mLauncher.getDeviceProfile().inv.isAllAppsButtonRank(
                             hotseat.getOrderInHotseat(mTargetCell[0], mTargetCell[1]))) {
                         return false;
                     }
@@ -2610,19 +2474,20 @@
         boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
         boolean willBecomeShortcut =
                 (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
-                        info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
+                        info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
+                        info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
 
         return (aboveShortcut && willBecomeShortcut);
     }
 
-    boolean willAddToExistingUserFolder(Object dragInfo, CellLayout target, int[] targetCell,
+    boolean willAddToExistingUserFolder(ItemInfo dragInfo, CellLayout target, int[] targetCell,
             float distance) {
         if (distance > mMaxDistanceForFolderCreation) return false;
         View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
         return willAddToExistingUserFolder(dragInfo, dropOverView);
 
     }
-    boolean willAddToExistingUserFolder(Object dragInfo, View dropOverView) {
+    boolean willAddToExistingUserFolder(ItemInfo dragInfo, View dropOverView) {
         if (dropOverView != null) {
             CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
             if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
@@ -2681,9 +2546,14 @@
             // If the dragView is null, we can't animate
             boolean animate = dragView != null;
             if (animate) {
+                // In order to keep everything continuous, we hand off the currently rendered
+                // folder background to the newly created icon. This preserves animation state.
+                fi.setFolderBackground(mFolderCreateBg);
+                mFolderCreateBg = new FolderIcon.PreviewBackground();
                 fi.performCreateAnimation(destInfo, v, sourceInfo, dragView, folderLocation, scale,
                         postAnimationRunnable);
             } else {
+                fi.prepareCreate(v);
                 fi.addItem(destInfo);
                 fi.addItem(sourceInfo);
             }
@@ -2727,11 +2597,11 @@
             if (mLauncher.isHotseatLayout(dropTargetLayout)) {
                 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
             } else {
-                mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
+                mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter);
             }
         }
 
-        int snapScreen = WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE;
+        int snapScreen = -1;
         boolean resizeOnDrop = false;
         if (d.dragSource != this) {
             final int[] touchXY = new int[] { (int) mDragViewVisualCenter[0],
@@ -2740,7 +2610,6 @@
         } else if (mDragInfo != null) {
             final View cell = mDragInfo.cell;
 
-            Runnable resizeRunnable = null;
             if (dropTargetLayout != null && !d.cancelled) {
                 // Move internally
                 boolean hasMovedLayouts = (getParentCellLayoutForView(cell) != dropTargetLayout);
@@ -2774,7 +2643,7 @@
 
                 // Aside from the special case where we're dropping a shortcut onto a shortcut,
                 // we need to find the nearest cell location that is vacant
-                ItemInfo item = (ItemInfo) d.dragInfo;
+                ItemInfo item = d.dragInfo;
                 int minSpanX = item.spanX;
                 int minSpanY = item.spanY;
                 if (item.minSpanX > 0 && item.minSpanY > 0) {
@@ -2812,7 +2681,7 @@
                         CellLayout parentCell = getParentCellLayoutForView(cell);
                         if (parentCell != null) {
                             parentCell.removeView(cell);
-                        } else if (LauncherAppState.isDogfoodBuild()) {
+                        } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
                             throw new NullPointerException("mDragInfo.cell has null parent");
                         }
                         addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
@@ -2837,21 +2706,14 @@
                         AppWidgetProviderInfo pInfo = hostView.getAppWidgetInfo();
                         if (pInfo != null && pInfo.resizeMode != AppWidgetProviderInfo.RESIZE_NONE
                                 && !d.accessibleDrag) {
-                            final Runnable addResizeFrame = new Runnable() {
+                            mDelayedResizeRunnable = new Runnable() {
                                 public void run() {
-                                    DragLayer dragLayer = mLauncher.getDragLayer();
-                                    dragLayer.addResizeFrame(info, hostView, cellLayout);
-                                }
-                            };
-                            resizeRunnable = (new Runnable() {
-                                public void run() {
-                                    if (!isPageMoving()) {
-                                        addResizeFrame.run();
-                                    } else {
-                                        mDelayedResizeRunnable = addResizeFrame;
+                                    if (!isPageMoving() && !mIsSwitchingState) {
+                                        DragLayer dragLayer = mLauncher.getDragLayer();
+                                        dragLayer.addResizeFrame(info, hostView, cellLayout);
                                     }
                                 }
-                            });
+                            };
                         }
                     }
 
@@ -2868,7 +2730,6 @@
             }
 
             final CellLayout parent = (CellLayout) cell.getParent().getParent();
-            final Runnable finalResizeRunnable = resizeRunnable;
             // Prepare it to be animated into its new position
             // This must be called after the view has been re-parented
             final Runnable onCompleteRunnable = new Runnable() {
@@ -2876,9 +2737,6 @@
                 public void run() {
                     mAnimatingViewIntoPlace = false;
                     updateChildrenLayersEnabled(false);
-                    if (finalResizeRunnable != null) {
-                        finalResizeRunnable.run();
-                    }
                 }
             };
             mAnimatingViewIntoPlace = true;
@@ -2892,9 +2750,7 @@
                     animateWidgetDrop(info, parent, d.dragView,
                             onCompleteRunnable, animationType, cell, false);
                 } else {
-                    int duration = snapScreen < 0 ?
-                            WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE :
-                                    ADJACENT_SCREEN_DROP_DURATION;
+                    int duration = snapScreen < 0 ? -1 : ADJACENT_SCREEN_DROP_DURATION;
                     mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, cell, duration,
                             onCompleteRunnable, this);
                 }
@@ -2904,6 +2760,9 @@
             }
             parent.onDropChild(cell);
         }
+        if (d.stateAnnouncer != null) {
+            d.stateAnnouncer.completeAction(R.string.item_moved);
+        }
     }
 
     /**
@@ -2928,19 +2787,6 @@
                 (int) (mTempXY[1] + scale * boundingLayout.getMeasuredHeight()));
     }
 
-    public void getViewLocationRelativeToSelf(View v, int[] location) {
-        getLocationInWindow(location);
-        int x = location[0];
-        int y = location[1];
-
-        v.getLocationInWindow(location);
-        int vX = location[0];
-        int vY = location[1];
-
-        location[0] = vX - x;
-        location[1] = vY - y;
-    }
-
     @Override
     public void onDragEnter(DragObject d) {
         if (ENFORCE_DRAG_EVENT_ORDER) {
@@ -2951,54 +2797,13 @@
         mAddToExistingFolderOnDrop = false;
 
         mDropToLayout = null;
-        CellLayout layout = getCurrentDropLayout();
-        setCurrentDropLayout(layout);
-        setCurrentDragOverlappingLayout(layout);
+        setDropLayoutForDragObject(d);
 
-        if (!workspaceInModalState()) {
+        if (!workspaceInModalState() && FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
             mLauncher.getDragLayer().showPageHints();
         }
     }
 
-    /** Return a rect that has the cellWidth/cellHeight (left, top), and
-     * widthGap/heightGap (right, bottom) */
-    static Rect getCellLayoutMetrics(Launcher launcher, int orientation) {
-        LauncherAppState app = LauncherAppState.getInstance();
-        InvariantDeviceProfile inv = app.getInvariantDeviceProfile();
-
-        Display display = launcher.getWindowManager().getDefaultDisplay();
-        Point smallestSize = new Point();
-        Point largestSize = new Point();
-        display.getCurrentSizeRange(smallestSize, largestSize);
-        int countX = (int) inv.numColumns;
-        int countY = (int) inv.numRows;
-        boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
-        if (orientation == CellLayout.LANDSCAPE) {
-            if (mLandscapeCellLayoutMetrics == null) {
-                Rect padding = inv.landscapeProfile.getWorkspacePadding(isLayoutRtl);
-                int width = largestSize.x - padding.left - padding.right;
-                int height = smallestSize.y - padding.top - padding.bottom;
-                mLandscapeCellLayoutMetrics = new Rect();
-                mLandscapeCellLayoutMetrics.set(
-                        DeviceProfile.calculateCellWidth(width, countX),
-                        DeviceProfile.calculateCellHeight(height, countY), 0, 0);
-            }
-            return mLandscapeCellLayoutMetrics;
-        } else if (orientation == CellLayout.PORTRAIT) {
-            if (mPortraitCellLayoutMetrics == null) {
-                Rect padding = inv.portraitProfile.getWorkspacePadding(isLayoutRtl);
-                int width = smallestSize.x - padding.left - padding.right;
-                int height = largestSize.y - padding.top - padding.bottom;
-                mPortraitCellLayoutMetrics = new Rect();
-                mPortraitCellLayoutMetrics.set(
-                        DeviceProfile.calculateCellWidth(width, countX),
-                        DeviceProfile.calculateCellHeight(height, countY), 0, 0);
-            }
-            return mPortraitCellLayoutMetrics;
-        }
-        return null;
-    }
-
     @Override
     public void onDragExit(DragObject d) {
         if (ENFORCE_DRAG_EVENT_ORDER) {
@@ -3075,7 +2880,13 @@
         if (mDragOverlappingLayout != null) {
             mDragOverlappingLayout.setIsDragOverlapping(true);
         }
-        invalidate();
+        // Invalidating the scrim will also force this CellLayout
+        // to be invalidated so that it is highlighted if necessary.
+        mLauncher.getDragLayer().invalidateScrim();
+    }
+
+    public CellLayout getCurrentDragOverlappingLayout() {
+        return mDragOverlappingLayout;
     }
 
     void setCurrentDropOverCell(int x, int y) {
@@ -3109,9 +2920,8 @@
     }
 
     private void cleanupFolderCreation() {
-        if (mDragFolderRingAnimator != null) {
-            mDragFolderRingAnimator.animateToNaturalState();
-            mDragFolderRingAnimator = null;
+        if (mFolderCreateBg != null) {
+            mFolderCreateBg.animateToRest();
         }
         mFolderCreationAlarm.setOnAlarmListener(null);
         mFolderCreationAlarm.cancelAlarm();
@@ -3119,7 +2929,7 @@
 
     private void cleanupAddToFolder() {
         if (mDragOverFolderIcon != null) {
-            mDragOverFolderIcon.onDragExit(null);
+            mDragOverFolderIcon.onDragExit();
             mDragOverFolderIcon = null;
         }
     }
@@ -3137,41 +2947,31 @@
     *
     * Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
     * coordinate space. The argument xy is modified with the return result.
-    *
-    * if cachedInverseMatrix is not null, this method will just use that matrix instead of
-    * computing it itself; we use this to avoid redundant matrix inversions in
-    * findMatchingPageForDragOver
-    *
     */
-   void mapPointFromSelfToChild(View v, float[] xy, Matrix cachedInverseMatrix) {
+   void mapPointFromSelfToChild(View v, float[] xy) {
        xy[0] = xy[0] - v.getLeft();
        xy[1] = xy[1] - v.getTop();
    }
 
-   boolean isPointInSelfOverHotseat(int x, int y, Rect r) {
-       if (r == null) {
-           r = new Rect();
-       }
-       mTempPt[0] = x;
-       mTempPt[1] = y;
-       mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
-
-       DeviceProfile grid = mLauncher.getDeviceProfile();
-       r = grid.getHotseatRect();
-       if (r.contains(mTempPt[0], mTempPt[1])) {
-           return true;
-       }
-       return false;
+   boolean isPointInSelfOverHotseat(int x, int y) {
+       mTempXY[0] = x;
+       mTempXY[1] = y;
+       mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true);
+       View hotseat = mLauncher.getHotseat();
+       return mTempXY[0] >= hotseat.getLeft() &&
+               mTempXY[0] <= hotseat.getRight() &&
+               mTempXY[1] >= hotseat.getTop() &&
+               mTempXY[1] <= hotseat.getBottom();
    }
 
    void mapPointFromSelfToHotseatLayout(Hotseat hotseat, float[] xy) {
-       mTempPt[0] = (int) xy[0];
-       mTempPt[1] = (int) xy[1];
-       mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempPt, true);
-       mLauncher.getDragLayer().mapCoordInSelfToDescendent(hotseat.getLayout(), mTempPt);
+       mTempXY[0] = (int) xy[0];
+       mTempXY[1] = (int) xy[1];
+       mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempXY, true);
+       mLauncher.getDragLayer().mapCoordInSelfToDescendent(hotseat.getLayout(), mTempXY);
 
-       xy[0] = mTempPt[0];
-       xy[1] = mTempPt[1];
+       xy[0] = mTempXY[0];
+       xy[1] = mTempXY[1];
    }
 
    /*
@@ -3217,10 +3017,7 @@
             CellLayout cl = (CellLayout) getChildAt(i);
 
             final float[] touchXy = {originX, originY};
-            // Transform the touch coordinates to the CellLayout's local coordinates
-            // If the touch point is within the bounds of the cell layout, we can return immediately
-            cl.getMatrix().invert(mTempInverseMatrix);
-            mapPointFromSelfToChild(cl, touchXy, mTempInverseMatrix);
+            mapPointFromSelfToChild(cl, touchXy);
 
             if (touchXy[0] >= 0 && touchXy[0] <= cl.getWidth() &&
                     touchXy[1] >= 0 && touchXy[1] <= cl.getHeight()) {
@@ -3254,19 +3051,14 @@
         return (d.dragInfo instanceof LauncherAppWidgetInfo ||
                 d.dragInfo instanceof PendingAddWidgetInfo);
     }
-    private boolean isExternalDragWidget(DragObject d) {
-        return d.dragSource != this && isDragWidget(d);
-    }
 
     public void onDragOver(DragObject d) {
         // Skip drag over events while we are dragging over side pages
         if (mInScrollArea || !transitionStateShouldAllowDrop()) return;
 
-        Rect r = new Rect();
-        CellLayout layout = null;
-        ItemInfo item = (ItemInfo) d.dragInfo;
+        ItemInfo item = d.dragInfo;
         if (item == null) {
-            if (LauncherAppState.isDogfoodBuild()) {
+            if (ProviderConfig.IS_DOGFOOD_BUILD) {
                 throw new NullPointerException("DragObject has null info");
             }
             return;
@@ -3277,43 +3069,15 @@
         mDragViewVisualCenter = d.getVisualCenter(mDragViewVisualCenter);
 
         final View child = (mDragInfo == null) ? null : mDragInfo.cell;
-        // Identify whether we have dragged over a side page
-        if (workspaceInModalState()) {
-            if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
-                if (isPointInSelfOverHotseat(d.x, d.y, r)) {
-                    layout = mLauncher.getHotseat().getLayout();
+        if (setDropLayoutForDragObject(d)) {
+            boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
+            if (isInSpringLoadedMode) {
+                if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
+                    mSpringLoadedDragController.cancel();
+                } else {
+                    mSpringLoadedDragController.setAlarm(mDragTargetLayout);
                 }
             }
-            if (layout == null) {
-                layout = findMatchingPageForDragOver(d.dragView, d.x, d.y, false);
-            }
-            if (layout != mDragTargetLayout) {
-                setCurrentDropLayout(layout);
-                setCurrentDragOverlappingLayout(layout);
-
-                boolean isInSpringLoadedMode = (mState == State.SPRING_LOADED);
-                if (isInSpringLoadedMode) {
-                    if (mLauncher.isHotseatLayout(layout)) {
-                        mSpringLoadedDragController.cancel();
-                    } else {
-                        mSpringLoadedDragController.setAlarm(mDragTargetLayout);
-                    }
-                }
-            }
-        } else {
-            // Test to see if we are over the hotseat otherwise just use the current page
-            if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
-                if (isPointInSelfOverHotseat(d.x, d.y, r)) {
-                    layout = mLauncher.getHotseat().getLayout();
-                }
-            }
-            if (layout == null) {
-                layout = getCurrentDropLayout();
-            }
-            if (layout != mDragTargetLayout) {
-                setCurrentDropLayout(layout);
-                setCurrentDragOverlappingLayout(layout);
-            }
         }
 
         // Handle the drag over
@@ -3322,7 +3086,7 @@
             if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
                 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
             } else {
-                mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
+                mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter);
             }
 
             int minSpanX = item.spanX;
@@ -3350,7 +3114,7 @@
                     item.spanY, child, mTargetCell);
 
             if (!nearestDropOccupied) {
-                mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
+                mDragTargetLayout.visualizeDropLocation(child, mOutlineProvider,
                         mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false, d);
             } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
                     && !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
@@ -3378,12 +3142,46 @@
         }
     }
 
+    /**
+     * Updates {@link #mDragTargetLayout} and {@link #mDragOverlappingLayout}
+     * based on the DragObject's position.
+     *
+     * The layout will be:
+     * - The Hotseat if the drag object is over it
+     * - A side page if we are in spring-loaded mode and the drag object is over it
+     * - The current page otherwise
+     *
+     * @return whether the layout is different from the current {@link #mDragTargetLayout}.
+     */
+    private boolean setDropLayoutForDragObject(DragObject d) {
+        CellLayout layout = null;
+        // Test to see if we are over the hotseat first
+        if (mLauncher.getHotseat() != null && !isDragWidget(d)) {
+            if (isPointInSelfOverHotseat(d.x, d.y)) {
+                layout = mLauncher.getHotseat().getLayout();
+            }
+        }
+        if (layout == null) {
+            // Identify whether we have dragged over a side page,
+            // otherwise just use the current page
+            layout = workspaceInModalState() ?
+                    findMatchingPageForDragOver(d.dragView, d.x, d.y, false)
+                    : getCurrentDropLayout();
+        }
+        if (layout != mDragTargetLayout) {
+            setCurrentDropLayout(layout);
+            setCurrentDragOverlappingLayout(layout);
+            return true;
+        }
+        return false;
+    }
+
     private void manageFolderFeedback(CellLayout targetLayout,
             int[] targetCell, float distance, DragObject dragObject) {
         if (distance > mMaxDistanceForFolderCreation) return;
 
         final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]);
-        ItemInfo info = (ItemInfo) dragObject.dragInfo;
+        ItemInfo info = dragObject.dragInfo;
         boolean userFolderPending = willCreateUserFolder(info, dragOverView, false);
         if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
                 !mFolderCreationAlarm.alarmPending()) {
@@ -3434,22 +3232,26 @@
         int cellX;
         int cellY;
 
+        FolderIcon.PreviewBackground bg = new FolderIcon.PreviewBackground();
+
         public FolderCreationAlarmListener(CellLayout layout, int cellX, int cellY) {
             this.layout = layout;
             this.cellX = cellX;
             this.cellY = cellY;
+
+            DeviceProfile grid = mLauncher.getDeviceProfile();
+            BubbleTextView cell = (BubbleTextView) layout.getChildAt(cellX, cellY);
+
+            bg.setup(getResources().getDisplayMetrics(), grid, null,
+                    cell.getMeasuredWidth(), cell.getPaddingTop());
+
+            // The full preview background should appear behind the icon
+            bg.isClipping = false;
         }
 
         public void onAlarm(Alarm alarm) {
-            if (mDragFolderRingAnimator != null) {
-                // This shouldn't happen ever, but just in case, make sure we clean up the mess.
-                mDragFolderRingAnimator.animateToNaturalState();
-            }
-            mDragFolderRingAnimator = new FolderRingAnimator(mLauncher, null);
-            mDragFolderRingAnimator.setCell(cellX, cellY);
-            mDragFolderRingAnimator.setCellLayout(layout);
-            mDragFolderRingAnimator.animateToAcceptState();
-            layout.showFolderAccept(mDragFolderRingAnimator);
+            mFolderCreateBg = bg;
+            mFolderCreateBg.animateToAccept(layout, cellX, cellY);
             layout.clearDragOutlines();
             setDragMode(DRAG_MODE_CREATE_FOLDER);
         }
@@ -3491,7 +3293,7 @@
             }
 
             boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
-            mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
+            mDragTargetLayout.visualizeDropLocation(child, mOutlineProvider,
                 mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize, dragObject);
         }
     }
@@ -3504,24 +3306,6 @@
     }
 
     /**
-     * Add the item specified by dragInfo to the given layout.
-     * @return true if successful
-     */
-    public boolean addExternalItemToScreen(ItemInfo dragInfo, CellLayout layout) {
-        if (layout.findCellForSpan(mTempEstimate, dragInfo.spanX, dragInfo.spanY)) {
-            onDropExternal(dragInfo.dropPos, (ItemInfo) dragInfo, (CellLayout) layout, false);
-            return true;
-        }
-        mLauncher.showOutOfSpaceMessage(mLauncher.isHotseatLayout(layout));
-        return false;
-    }
-
-    private void onDropExternal(int[] touchXY, Object dragInfo,
-            CellLayout cellLayout, boolean insertAtFirst) {
-        onDropExternal(touchXY, dragInfo, cellLayout, insertAtFirst, null);
-    }
-
-    /**
      * Drop an item that didn't originate on one of the workspace screens.
      * It may have come from Launcher (e.g. from all apps or customize), or it may have
      * come from another app altogether.
@@ -3529,7 +3313,7 @@
      * NOTE: This can also be called when we are outside of a drag event, when we want
      * to add an item to one of the workspace screens.
      */
-    private void onDropExternal(final int[] touchXY, final Object dragInfo,
+    private void onDropExternal(final int[] touchXY, final ItemInfo dragInfo,
             final CellLayout cellLayout, boolean insertAtFirst, DragObject d) {
         final Runnable exitSpringLoadedRunnable = new Runnable() {
             @Override
@@ -3539,7 +3323,7 @@
             }
         };
 
-        ItemInfo info = (ItemInfo) dragInfo;
+        ItemInfo info = dragInfo;
         int spanX = info.spanX;
         int spanY = info.spanY;
         if (mDragInfo != null) {
@@ -3566,14 +3350,14 @@
                         cellLayout, mTargetCell);
                 float distance = cellLayout.getDistanceFromCell(mDragViewVisualCenter[0],
                         mDragViewVisualCenter[1], mTargetCell);
-                if (willCreateUserFolder((ItemInfo) d.dragInfo, cellLayout, mTargetCell,
-                        distance, true) || willAddToExistingUserFolder((ItemInfo) d.dragInfo,
-                                cellLayout, mTargetCell, distance)) {
+                if (willCreateUserFolder(d.dragInfo, cellLayout, mTargetCell, distance, true)
+                        || willAddToExistingUserFolder(
+                                d.dragInfo, cellLayout, mTargetCell, distance)) {
                     findNearestVacantCell = false;
                 }
             }
 
-            final ItemInfo item = (ItemInfo) d.dragInfo;
+            final ItemInfo item = d.dragInfo;
             boolean updateWidgetSize = false;
             if (findNearestVacantCell) {
                 int minSpanX = item.spanX;
@@ -3633,9 +3417,11 @@
             switch (info.itemType) {
             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                 if (info.container == NO_ID && info instanceof AppInfo) {
                     // Came from all apps -- make a copy
                     info = ((AppInfo) info).makeShortcut();
+                    d.dragInfo = info;
                 }
                 view = mLauncher.createShortcut(cellLayout, (ShortcutInfo) info);
                 break;
@@ -3818,6 +3604,10 @@
         }
     }
 
+    public WorkspaceStateTransitionAnimation getStateTransitionAnimation() {
+        return mStateTransitionAnimation;
+    }
+
     /**
      * Return the current {@link CellLayout}, correctly picking the destination
      * screen while a scroll is in progress.
@@ -3886,7 +3676,7 @@
                     mDragInfo.container, mDragInfo.screenId);
             if (cellLayout != null) {
                 cellLayout.onDropChild(mDragInfo.cell);
-            } else if (LauncherAppState.isDogfoodBuild()) {
+            } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
                 throw new RuntimeException("Invalid state: cellLayout == null in "
                         + "Workspace#onDropCompleted. Please file a bug. ");
             };
@@ -3895,8 +3685,15 @@
                 && mDragInfo.cell != null) {
             mDragInfo.cell.setVisibility(VISIBLE);
         }
-        mDragOutline = null;
+        mOutlineProvider = null;
         mDragInfo = null;
+
+        if (!isFlingToDelete) {
+            // Fling to delete already exits spring loaded mode after the animation finishes.
+            mLauncher.exitSpringLoadedDragModeDelayed(success,
+                    Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, mDelayedResizeRunnable);
+            mDelayedResizeRunnable = null;
+        }
     }
 
     /**
@@ -3906,7 +3703,7 @@
         CellLayout parentCell = getParentCellLayoutForView(v);
         if (parentCell != null) {
             parentCell.removeView(v);
-        } else if (LauncherAppState.isDogfoodBuild()) {
+        } else if (ProviderConfig.IS_DOGFOOD_BUILD) {
             // When an app is uninstalled using the drop target, we wait until resume to remove
             // the icon. We also remove all the corresponding items from the workspace at
             // {@link Launcher#bindComponentsRemoved}. That call can come before or after
@@ -3918,6 +3715,21 @@
         }
     }
 
+    /**
+     * Removes all folder listeners
+     */
+    public void removeFolderListeners() {
+        mapOverItems(false, new ItemOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View view) {
+                if (view instanceof FolderIcon) {
+                    ((FolderIcon) view).removeListeners();
+                }
+                return false;
+            }
+        });
+    }
+
     @Override
     public void deferCompleteDropAfterUninstallActivity() {
         mDeferDropAfterUninstall = true;
@@ -3925,7 +3737,7 @@
 
     /// maybe move this into a smaller part
     @Override
-    public void onUninstallActivityReturned(boolean success) {
+    public void onDragObjectRemoved(boolean success) {
         mDeferDropAfterUninstall = false;
         mUninstallSuccessful = success;
         if (mDeferredAction != null) {
@@ -3933,70 +3745,6 @@
         }
     }
 
-    void updateItemLocationsInDatabase(CellLayout cl) {
-        int count = cl.getShortcutsAndWidgets().getChildCount();
-
-        long screenId = getIdForScreen(cl);
-        int container = Favorites.CONTAINER_DESKTOP;
-
-        if (mLauncher.isHotseatLayout(cl)) {
-            screenId = -1;
-            container = Favorites.CONTAINER_HOTSEAT;
-        }
-
-        for (int i = 0; i < count; i++) {
-            View v = cl.getShortcutsAndWidgets().getChildAt(i);
-            ItemInfo info = (ItemInfo) v.getTag();
-            // Null check required as the AllApps button doesn't have an item info
-            if (info != null && info.requiresDbUpdate) {
-                info.requiresDbUpdate = false;
-                LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, info.cellX,
-                        info.cellY, info.spanX, info.spanY);
-            }
-        }
-    }
-
-    void saveWorkspaceToDb() {
-        saveWorkspaceScreenToDb((CellLayout) mLauncher.getHotseat().getLayout());
-        int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            CellLayout cl = (CellLayout) getChildAt(i);
-            saveWorkspaceScreenToDb(cl);
-        }
-    }
-
-    void saveWorkspaceScreenToDb(CellLayout cl) {
-        int count = cl.getShortcutsAndWidgets().getChildCount();
-
-        long screenId = getIdForScreen(cl);
-        int container = Favorites.CONTAINER_DESKTOP;
-
-        Hotseat hotseat = mLauncher.getHotseat();
-        if (mLauncher.isHotseatLayout(cl)) {
-            screenId = -1;
-            container = Favorites.CONTAINER_HOTSEAT;
-        }
-
-        for (int i = 0; i < count; i++) {
-            View v = cl.getShortcutsAndWidgets().getChildAt(i);
-            ItemInfo info = (ItemInfo) v.getTag();
-            // Null check required as the AllApps button doesn't have an item info
-            if (info != null) {
-                int cellX = info.cellX;
-                int cellY = info.cellY;
-                if (container == Favorites.CONTAINER_HOTSEAT) {
-                    cellX = hotseat.getCellXFromOrder((int) info.screenId);
-                    cellY = hotseat.getCellYFromOrder((int) info.screenId);
-                }
-                LauncherModel.addItemToDatabase(mLauncher, info, container, screenId, cellX, cellY);
-            }
-            if (v instanceof FolderIcon) {
-                FolderIcon fi = (FolderIcon) v;
-                fi.getFolder().addItemLocationsInDatabase();
-            }
-        }
-    }
-
     @Override
     public float getIntrinsicIconScaleFactor() {
         return 1f;
@@ -4009,7 +3757,7 @@
 
     @Override
     public boolean supportsAppInfoDropTarget() {
-        return false;
+        return !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND;
     }
 
     @Override
@@ -4175,8 +3923,7 @@
      * the hotseat and workspace pages
      */
     ArrayList<ShortcutAndWidgetContainer> getAllShortcutAndWidgetContainers() {
-        ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
-                new ArrayList<ShortcutAndWidgetContainer>();
+        ArrayList<ShortcutAndWidgetContainer> childrenLayouts = new ArrayList<>();
         int screenCount = getChildCount();
         for (int screen = 0; screen < screenCount; screen++) {
             childrenLayouts.add(((CellLayout) getChildAt(screen)).getShortcutsAndWidgets());
@@ -4187,22 +3934,11 @@
         return childrenLayouts;
     }
 
-    public Folder getFolderForTag(final Object tag) {
-        return (Folder) getFirstMatch(new ItemOperator() {
-
-            @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
-                return (v instanceof Folder) && (((Folder) v).getInfo() == tag)
-                        && ((Folder) v).getInfo().opened;
-            }
-        });
-    }
-
     public View getHomescreenIconByItemId(final long id) {
         return getFirstMatch(new ItemOperator() {
 
             @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
+            public boolean evaluate(ItemInfo info, View v) {
                 return info != null && info.id == id;
             }
         });
@@ -4212,7 +3948,7 @@
         return getFirstMatch(new ItemOperator() {
 
             @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
+            public boolean evaluate(ItemInfo info, View v) {
                 return info == tag;
             }
         });
@@ -4222,19 +3958,19 @@
         return (LauncherAppWidgetHostView) getFirstMatch(new ItemOperator() {
 
             @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
+            public boolean evaluate(ItemInfo info, View v) {
                 return (info instanceof LauncherAppWidgetInfo) &&
                         ((LauncherAppWidgetInfo) info).appWidgetId == appWidgetId;
             }
         });
     }
 
-    private View getFirstMatch(final ItemOperator operator) {
+    public View getFirstMatch(final ItemOperator operator) {
         final View[] value = new View[1];
         mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
             @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
-                if (operator.evaluate(info, v, parent)) {
+            public boolean evaluate(ItemInfo info, View v) {
+                if (operator.evaluate(info, v)) {
                     value[0] = v;
                     return true;
                 }
@@ -4247,7 +3983,7 @@
     void clearDropTargets() {
         mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
             @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
+            public boolean evaluate(ItemInfo info, View v) {
                 if (v instanceof DropTarget) {
                     mDragController.removeDropTarget((DropTarget) v);
                 }
@@ -4257,66 +3993,30 @@
         });
     }
 
-    // Removes ALL items that match a given package name, this is usually called when a package
-    // has been removed and we want to remove all components (widgets, shortcuts, apps) that
-    // belong to that package.
-    void removeItemsByPackageName(final HashSet<String> packageNames, final UserHandleCompat user) {
-        // Filter out all the ItemInfos that this is going to affect
-        final HashSet<ItemInfo> infos = new HashSet<ItemInfo>();
-        final HashSet<ComponentName> cns = new HashSet<ComponentName>();
-        ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
-        for (CellLayout layoutParent : cellLayouts) {
-            ViewGroup layout = layoutParent.getShortcutsAndWidgets();
-            int childCount = layout.getChildCount();
-            for (int i = 0; i < childCount; ++i) {
-                View view = layout.getChildAt(i);
-                infos.add((ItemInfo) view.getTag());
-            }
-        }
-        LauncherModel.ItemInfoFilter filter = new LauncherModel.ItemInfoFilter() {
-            @Override
-            public boolean filterItem(ItemInfo parent, ItemInfo info,
-                                      ComponentName cn) {
-                if (packageNames.contains(cn.getPackageName())
-                        && info.user.equals(user)) {
-                    cns.add(cn);
-                    return true;
-                }
-                return false;
-            }
-        };
-        LauncherModel.filterItemInfos(infos, filter);
-
-        // Remove the affected components
-        removeItemsByComponentName(cns, user);
-    }
-
     /**
-     * Removes items that match the item info specified. When applications are removed
+     * Removes items that match the {@param matcher}. When applications are removed
      * as a part of an update, this is called to ensure that other widgets and application
      * shortcuts are not removed.
      */
-    void removeItemsByComponentName(final HashSet<ComponentName> componentNames,
-            final UserHandleCompat user) {
+    public void removeItemsByMatcher(final ItemInfoMatcher matcher) {
         ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
         for (final CellLayout layoutParent: cellLayouts) {
             final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
 
-            final HashMap<ItemInfo, View> children = new HashMap<ItemInfo, View>();
+            final HashMap<ItemInfo, View> children = new HashMap<>();
             for (int j = 0; j < layout.getChildCount(); j++) {
                 final View view = layout.getChildAt(j);
                 children.put((ItemInfo) view.getTag(), view);
             }
 
-            final ArrayList<View> childrenToRemove = new ArrayList<View>();
-            final HashMap<FolderInfo, ArrayList<ShortcutInfo>> folderAppsToRemove =
-                    new HashMap<FolderInfo, ArrayList<ShortcutInfo>>();
+            final ArrayList<View> childrenToRemove = new ArrayList<>();
+            final HashMap<FolderInfo, ArrayList<ShortcutInfo>> folderAppsToRemove = new HashMap<>();
             LauncherModel.ItemInfoFilter filter = new LauncherModel.ItemInfoFilter() {
                 @Override
                 public boolean filterItem(ItemInfo parent, ItemInfo info,
-                                          ComponentName cn) {
+                        ComponentName cn) {
                     if (parent instanceof FolderInfo) {
-                        if (componentNames.contains(cn) && info.user.equals(user)) {
+                        if (matcher.matches(info, cn)) {
                             FolderInfo folder = (FolderInfo) parent;
                             ArrayList<ShortcutInfo> appsToRemove;
                             if (folderAppsToRemove.containsKey(folder)) {
@@ -4329,7 +4029,7 @@
                             return true;
                         }
                     } else {
-                        if (componentNames.contains(cn) && info.user.equals(user)) {
+                        if (matcher.matches(info, cn)) {
                             childrenToRemove.add(children.get(info));
                             return true;
                         }
@@ -4343,7 +4043,7 @@
             for (FolderInfo folder : folderAppsToRemove.keySet()) {
                 ArrayList<ShortcutInfo> appsToRemove = folderAppsToRemove.get(folder);
                 for (ShortcutInfo info : appsToRemove) {
-                    folder.remove(info);
+                    folder.remove(info, false);
                 }
             }
 
@@ -4367,16 +4067,15 @@
         stripEmptyScreens();
     }
 
-    interface ItemOperator {
+    public interface ItemOperator {
         /**
-         * Process the next itemInfo, possibly with side-effect on {@link ItemOperator#value}.
+         * Process the next itemInfo, possibly with side-effect on the next item.
          *
          * @param info info for the shortcut
          * @param view view for the shortcut
-         * @param parent containing folder, or null
          * @return true if done, false to continue the map
          */
-        public boolean evaluate(ItemInfo info, View view, View parent);
+        public boolean evaluate(ItemInfo info, View view);
     }
 
     /**
@@ -4403,12 +4102,12 @@
                     for (int childIdx = 0; childIdx < childCount; childIdx++) {
                         View child = folderChildren.get(childIdx);
                         info = (ItemInfo) child.getTag();
-                        if (op.evaluate(info, child, folder)) {
+                        if (op.evaluate(info, child)) {
                             return;
                         }
                     }
                 } else {
-                    if (op.evaluate(info, item, null)) {
+                    if (op.evaluate(info, item)) {
                         return;
                     }
                 }
@@ -4417,10 +4116,19 @@
     }
 
     void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {
-        final HashSet<ShortcutInfo> updates = new HashSet<ShortcutInfo>(shortcuts);
+        int total  = shortcuts.size();
+        final HashSet<ShortcutInfo> updates = new HashSet<ShortcutInfo>(total);
+        final HashSet<Long> folderIds = new HashSet<>();
+
+        for (int i = 0; i < total; i++) {
+            ShortcutInfo s = shortcuts.get(i);
+            updates.add(s);
+            folderIds.add(s.container);
+        }
+
         mapOverItems(MAP_RECURSE, new ItemOperator() {
             @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
+            public boolean evaluate(ItemInfo info, View v) {
                 if (info instanceof ShortcutInfo && v instanceof BubbleTextView &&
                         updates.contains(info)) {
                     ShortcutInfo si = (ShortcutInfo) info;
@@ -4430,10 +4138,18 @@
                             && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
                     shortcut.applyFromShortcutInfo(si, mIconCache,
                             si.isPromise() != oldPromiseState);
+                }
+                // process all the shortcuts
+                return false;
+            }
+        });
 
-                    if (parent != null) {
-                        parent.invalidate();
-                    }
+        // Update folder icons
+        mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View v) {
+                if (info instanceof FolderInfo && folderIds.contains(info.id)) {
+                    ((FolderInfo) info).itemsChanged(false);
                 }
                 // process all the shortcuts
                 return false;
@@ -4445,13 +4161,13 @@
         HashSet<String> packages = new HashSet<>(1);
         packages.add(packageName);
         LauncherModel.deletePackageFromDatabase(mLauncher, packageName, user);
-        removeItemsByPackageName(packages, user);
+        removeItemsByMatcher(ItemInfoMatcher.ofPackages(packages, user));
     }
 
     public void updateRestoreItems(final HashSet<ItemInfo> updates) {
         mapOverItems(MAP_RECURSE, new ItemOperator() {
             @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
+            public boolean evaluate(ItemInfo info, View v) {
                 if (info instanceof ShortcutInfo && v instanceof BubbleTextView
                         && updates.contains(info)) {
                     ((BubbleTextView) v).applyState(false);
@@ -4466,7 +4182,7 @@
         });
     }
 
-    public void widgetsRestored(ArrayList<LauncherAppWidgetInfo> changedInfo) {
+    public void widgetsRestored(final ArrayList<LauncherAppWidgetInfo> changedInfo) {
         if (!changedInfo.isEmpty()) {
             DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
                     mLauncher.getAppWidgetHost());
@@ -4487,12 +4203,18 @@
             } else {
                 // widgetRefresh will automatically run when the packages are updated.
                 // For now just update the progress bars
-                for (LauncherAppWidgetInfo info : changedInfo) {
-                    if (info.hostView instanceof PendingAppWidgetHostView) {
-                        info.installProgress = 100;
-                        ((PendingAppWidgetHostView) info.hostView).applyState();
+                mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
+                    @Override
+                    public boolean evaluate(ItemInfo info, View view) {
+                        if (view instanceof PendingAppWidgetHostView
+                                && changedInfo.contains(info)) {
+                            ((LauncherAppWidgetInfo) info).installProgress = 100;
+                            ((PendingAppWidgetHostView) view).applyState();
+                        }
+                        // process all the shortcuts
+                        return false;
                     }
-                }
+                });
             }
         }
     }
@@ -4512,7 +4234,7 @@
     }
 
     void moveToDefaultScreen(boolean animate) {
-        moveToScreen(mDefaultPage, animate);
+        moveToScreen(getDefaultPage(), animate);
     }
 
     void moveToCustomContentScreen(boolean animate) {
@@ -4532,24 +4254,11 @@
     }
 
     @Override
-    protected PageIndicator.PageMarkerResources getPageIndicatorMarker(int pageIndex) {
-        long screenId = getScreenIdForPageIndex(pageIndex);
-        if (screenId == EXTRA_EMPTY_SCREEN_ID) {
-            int count = mScreenOrder.size() - numCustomPages();
-            if (count > 1) {
-                return new PageIndicator.PageMarkerResources(R.drawable.ic_pageindicator_current,
-                        R.drawable.ic_pageindicator_add);
-            }
-        }
-
-        return super.getPageIndicatorMarker(pageIndex);
-    }
-
     protected String getPageIndicatorDescription() {
-        String settings = getResources().getString(R.string.settings_button_text);
-        return getCurrentPageDescription() + ", " + settings;
+        return getResources().getString(R.string.all_apps_button_label);
     }
 
+    @Override
     protected String getCurrentPageDescription() {
         if (hasCustomContent() && getNextPage() == 0) {
             return mCustomContentDescription;
@@ -4574,17 +4283,20 @@
         }
         return getContext().getString(R.string.workspace_scroll_format,
                 page + 1 - delta, nScreens);
-
-    }
-
-    public void getLocationInDragLayer(int[] loc) {
-        mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
     }
 
     @Override
-    public void fillInLaunchSourceData(View v, Bundle sourceData) {
-        sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_HOMESCREEN);
-        sourceData.putInt(Stats.SOURCE_EXTRA_CONTAINER_PAGE, getCurrentPage());
+    public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
+        target.gridX = info.cellX;
+        target.gridY = info.cellY;
+        target.pageIndex = getCurrentPage();
+        targetParent.containerType = LauncherLogProto.WORKSPACE;
+        if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
+            target.rank = info.rank;
+            targetParent.containerType = LauncherLogProto.HOTSEAT;
+        } else if (info.container >= 0) {
+            targetParent.containerType = LauncherLogProto.FOLDER;
+        }
     }
 
     /**
@@ -4622,14 +4334,32 @@
 
             mRefreshPending = false;
 
-            for (LauncherAppWidgetInfo info : mInfos) {
-                if (info.hostView instanceof PendingAppWidgetHostView) {
-                    // Remove and rebind the current widget, but don't delete it from the database
-                    PendingAppWidgetHostView view = (PendingAppWidgetHostView) info.hostView;
-                    mLauncher.removeItem(view, info, false /* deleteFromDb */);
-                    mLauncher.bindAppWidget(info);
+            mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
+                @Override
+                public boolean evaluate(ItemInfo info, View view) {
+                    if (view instanceof PendingAppWidgetHostView && mInfos.contains(info)) {
+                        PendingAppWidgetHostView hostView = (PendingAppWidgetHostView) view;
+                        mLauncher.removeItem(view, info, false /* deleteFromDb */);
+                        mLauncher.bindAppWidget((LauncherAppWidgetInfo) info);
+                    }
+                    // process all the shortcuts
+                    return false;
                 }
-            }
+            });
         }
     }
+
+    public interface OnStateChangeListener {
+
+        /**
+         * Called when the workspace state is changing.
+         * @param toState final state
+         * @param targetAnim animation which will be played during the transition or null.
+         */
+        void prepareStateChange(State toState, AnimatorSet targetAnim);
+    }
+
+    public static final boolean isQsbContainerPage(int pageNo) {
+        return pageNo == 0;
+    }
 }
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 54f63bb..598ba74 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -30,6 +30,8 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.DecelerateInterpolator;
 
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.util.Thunk;
 
 import java.util.HashMap;
@@ -42,6 +44,7 @@
 
     private View mView;
     private boolean mAccessibilityEnabled;
+    private boolean mCanceled = false;
 
     public AlphaUpdateListener(View v, boolean accessibilityEnabled) {
         mView = v;
@@ -66,7 +69,13 @@
     }
 
     @Override
+    public void onAnimationCancel(Animator animation) {
+        mCanceled = true;
+    }
+
+    @Override
     public void onAnimationEnd(Animator arg0) {
+        if (mCanceled) return;
         updateVisibility(mView, mAccessibilityEnabled);
     }
 
@@ -163,7 +172,7 @@
         workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden);
         overviewToWorkspace = (oldStateIsOverview && stateIsNormal);
         overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden);
-        allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal);
+        allAppsToWorkspace = (oldStateIsNormalHidden && stateIsNormal);
     }
 }
 
@@ -174,18 +183,12 @@
 
     public static final String TAG = "WorkspaceStateTransitionAnimation";
 
-    public static final int SCROLL_TO_CURRENT_PAGE = -1;
     @Thunk static final int BACKGROUND_FADE_OUT_DURATION = 350;
 
     final @Thunk Launcher mLauncher;
     final @Thunk Workspace mWorkspace;
 
     @Thunk AnimatorSet mStateAnimator;
-    @Thunk float[] mOldBackgroundAlphas;
-    @Thunk float[] mOldAlphas;
-    @Thunk float[] mNewBackgroundAlphas;
-    @Thunk float[] mNewAlphas;
-    @Thunk int mLastChildCount = -1;
 
     @Thunk float mCurrentScale;
     @Thunk float mNewScale;
@@ -198,6 +201,7 @@
     @Thunk int mAllAppsTransitionTime;
     @Thunk int mOverviewTransitionTime;
     @Thunk int mOverlayTransitionTime;
+    @Thunk int mSpringLoadedTransitionTime;
     @Thunk boolean mWorkspaceFadeInAdjacentScreens;
 
     public WorkspaceStateTransitionAnimation(Launcher launcher, Workspace workspace) {
@@ -209,22 +213,26 @@
         mAllAppsTransitionTime = res.getInteger(R.integer.config_allAppsTransitionTime);
         mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime);
         mOverlayTransitionTime = res.getInteger(R.integer.config_overlayTransitionTime);
-        mSpringLoadedShrinkFactor =
-                res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100f;
+        mSpringLoadedTransitionTime = mOverlayTransitionTime / 2;
+        mSpringLoadedShrinkFactor = mLauncher.getDeviceProfile().workspaceSpringLoadShrinkFactor;
         mOverviewModeShrinkFactor =
                 res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f;
         mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha) / 100f;
         mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
     }
 
+    public void snapToPageFromOverView(int whichPage) {
+        mWorkspace.snapToPage(whichPage, mOverviewTransitionTime, mZoomInInterpolator);
+    }
+
     public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState,
-            int toPage, boolean animated, HashMap<View, Integer> layerViews) {
+            boolean animated, HashMap<View, Integer> layerViews) {
         AccessibilityManager am = (AccessibilityManager)
                 mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE);
         final boolean accessibilityEnabled = am.isEnabled();
         TransitionStates states = new TransitionStates(fromState, toState);
         int workspaceDuration = getAnimationDuration(states);
-        animateWorkspace(states, toPage, animated, workspaceDuration, layerViews,
+        animateWorkspace(states, animated, workspaceDuration, layerViews,
                 accessibilityEnabled);
         animateBackgroundGradient(states, animated, BACKGROUND_FADE_OUT_DURATION);
         return mStateAnimator;
@@ -235,19 +243,6 @@
     }
 
     /**
-     * Reinitializes the arrays that we need for the animations on each page.
-     */
-    private void reinitializeAnimationArrays() {
-        final int childCount = mWorkspace.getChildCount();
-        if (mLastChildCount == childCount) return;
-
-        mOldBackgroundAlphas = new float[childCount];
-        mOldAlphas = new float[childCount];
-        mNewBackgroundAlphas = new float[childCount];
-        mNewAlphas = new float[childCount];
-    }
-
-    /**
      * Returns the proper animation duration for a transition.
      */
     private int getAnimationDuration(TransitionStates states) {
@@ -255,6 +250,9 @@
             return mAllAppsTransitionTime;
         } else if (states.workspaceToOverview || states.overviewToWorkspace) {
             return mOverviewTransitionTime;
+        } else if (mLauncher.mState == Launcher.State.WORKSPACE_SPRING_LOADED
+                || states.oldStateIsNormal && states.stateIsSpringLoaded) {
+            return mSpringLoadedTransitionTime;
         } else {
             return mOverlayTransitionTime;
         }
@@ -263,12 +261,9 @@
     /**
      * Starts a transition animation for the workspace.
      */
-    private void animateWorkspace(final TransitionStates states, int toPage, final boolean animated,
+    private void animateWorkspace(final TransitionStates states, final boolean animated,
                                   final int duration, final HashMap<View, Integer> layerViews,
                                   final boolean accessibilityEnabled) {
-        // Reinitialize animation arrays for the current workspace state
-        reinitializeAnimationArrays();
-
         // Cancel existing workspace animations and create a new animator set if requested
         cancelAnimation();
         if (animated) {
@@ -278,11 +273,18 @@
         // Update the workspace state
         float finalBackgroundAlpha = (states.stateIsSpringLoaded || states.stateIsOverview) ?
                 1.0f : 0f;
-        float finalHotseatAndPageIndicatorAlpha = (states.stateIsNormal || states.stateIsSpringLoaded) ?
-                1f : 0f;
+        float finalHotseatAlpha = (states.stateIsNormal || states.stateIsSpringLoaded ||
+                (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && states.stateIsNormalHidden)) ? 1f : 0f;
         float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f;
-        float finalWorkspaceTranslationY = states.stateIsOverview || states.stateIsOverviewHidden ?
-                mWorkspace.getOverviewModeTranslationY() : 0;
+        float finalQsbAlpha = (states.stateIsNormal ||
+                (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && states.stateIsNormalHidden)) ? 1f : 0f;
+
+        float finalWorkspaceTranslationY = 0;
+        if (states.stateIsOverview || states.stateIsOverviewHidden) {
+            finalWorkspaceTranslationY = mWorkspace.getOverviewModeTranslationY();
+        } else if (states.stateIsSpringLoaded) {
+            finalWorkspaceTranslationY = mWorkspace.getSpringLoadedTranslationY();
+        }
 
         final int childCount = mWorkspace.getChildCount();
         final int customPageCount = mWorkspace.numCustomPages();
@@ -303,18 +305,17 @@
             }
         }
 
-        if (toPage == SCROLL_TO_CURRENT_PAGE) {
-            toPage = mWorkspace.getPageNearestToCenterOfScreen();
-        }
-        mWorkspace.snapToPage(toPage, duration, mZoomInInterpolator);
-
+        int toPage = mWorkspace.getPageNearestToCenterOfScreen();
+        // TODO: Animate the celllayout alpha instead of the pages.
         for (int i = 0; i < childCount; i++) {
             final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i);
-            boolean isCurrentPage = (i == toPage);
             float initialAlpha = cl.getShortcutsAndWidgets().getAlpha();
             float finalAlpha;
-            if (states.stateIsNormalHidden || states.stateIsOverviewHidden) {
+            if (states.stateIsOverviewHidden) {
                 finalAlpha = 0f;
+            } else if(states.stateIsNormalHidden) {
+                finalAlpha = (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP &&
+                        i == mWorkspace.getNextPage()) ? 1 : 0;
             } else if (states.stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
                 finalAlpha = (i == toPage || i < customPageCount) ? 1f : 0f;
             } else {
@@ -323,8 +324,9 @@
 
             // If we are animating to/from the small state, then hide the side pages and fade the
             // current page in
-            if (!mWorkspace.isSwitchingState()) {
+            if (!FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && !mWorkspace.isSwitchingState()) {
                 if (states.workspaceToAllApps || states.allAppsToWorkspace) {
+                    boolean isCurrentPage = (i == toPage);
                     if (states.allAppsToWorkspace && isCurrentPage) {
                         initialAlpha = 0f;
                     } else if (!isCurrentPage) {
@@ -334,20 +336,49 @@
                 }
             }
 
-            mOldAlphas[i] = initialAlpha;
-            mNewAlphas[i] = finalAlpha;
             if (animated) {
-                mOldBackgroundAlphas[i] = cl.getBackgroundAlpha();
-                mNewBackgroundAlphas[i] = finalBackgroundAlpha;
+                float oldBackgroundAlpha = cl.getBackgroundAlpha();
+                if (initialAlpha != finalAlpha) {
+                    LauncherViewPropertyAnimator alphaAnim =
+                            new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets());
+                    alphaAnim.alpha(finalAlpha)
+                            .setDuration(duration)
+                            .setInterpolator(mZoomInInterpolator);
+                    mStateAnimator.play(alphaAnim);
+                }
+                if (oldBackgroundAlpha != 0 || finalBackgroundAlpha != 0) {
+                    ValueAnimator bgAnim = ObjectAnimator.ofFloat(cl, "backgroundAlpha",
+                            oldBackgroundAlpha, finalBackgroundAlpha);
+                    bgAnim.setInterpolator(mZoomInInterpolator);
+                    bgAnim.setDuration(duration);
+                    mStateAnimator.play(bgAnim);
+                }
             } else {
                 cl.setBackgroundAlpha(finalBackgroundAlpha);
                 cl.setShortcutAndWidgetAlpha(finalAlpha);
             }
+
+            if (Workspace.isQsbContainerPage(i)) {
+                if (animated) {
+                    Animator anim = mWorkspace.mQsbAlphaController
+                            .animateAlphaAtIndex(finalAlpha, Workspace.QSB_ALPHA_INDEX_PAGE_SCROLL);
+                    anim.setDuration(duration);
+                    anim.setInterpolator(mZoomInInterpolator);
+                    mStateAnimator.play(anim);
+                } else {
+                    mWorkspace.mQsbAlphaController.setAlphaAtIndex(
+                            finalAlpha, Workspace.QSB_ALPHA_INDEX_PAGE_SCROLL);
+                }
+            }
         }
 
         final ViewGroup overviewPanel = mLauncher.getOverviewPanel();
-        final View hotseat = mLauncher.getHotseat();
-        final View pageIndicator = mWorkspace.getPageIndicator();
+
+        final View qsbContainer = mLauncher.getQsbContainer();
+
+        Animator qsbAlphaAnimation = mWorkspace.mQsbAlphaController
+                .animateAlphaAtIndex(finalQsbAlpha, Workspace.QSB_ALPHA_INDEX_STATE_CHANGE);
+
         if (animated) {
             LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(mWorkspace);
             scale.scaleX(mNewScale)
@@ -356,93 +387,48 @@
                     .setDuration(duration)
                     .setInterpolator(mZoomInInterpolator);
             mStateAnimator.play(scale);
-            for (int index = 0; index < childCount; index++) {
-                final int i = index;
-                final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i);
-                float currentAlpha = cl.getShortcutsAndWidgets().getAlpha();
-                if (mOldAlphas[i] == 0 && mNewAlphas[i] == 0) {
-                    cl.setBackgroundAlpha(mNewBackgroundAlphas[i]);
-                    cl.setShortcutAndWidgetAlpha(mNewAlphas[i]);
-                } else {
-                    if (layerViews != null) {
-                        layerViews.put(cl, LauncherStateTransitionAnimation.BUILD_LAYER);
-                    }
-                    if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) {
-                        LauncherViewPropertyAnimator alphaAnim =
-                                new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets());
-                        alphaAnim.alpha(mNewAlphas[i])
-                                .setDuration(duration)
-                                .setInterpolator(mZoomInInterpolator);
-                        mStateAnimator.play(alphaAnim);
-                    }
-                    if (mOldBackgroundAlphas[i] != 0 ||
-                            mNewBackgroundAlphas[i] != 0) {
-                        ValueAnimator bgAnim = ObjectAnimator.ofFloat(cl, "backgroundAlpha",
-                                mOldBackgroundAlphas[i], mNewBackgroundAlphas[i]);
-                                LauncherAnimUtils.ofFloat(cl, 0f, 1f);
-                        bgAnim.setInterpolator(mZoomInInterpolator);
-                        bgAnim.setDuration(duration);
-                        mStateAnimator.play(bgAnim);
-                    }
-                }
-            }
-            Animator pageIndicatorAlpha;
-            if (pageIndicator != null) {
-                pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator)
-                        .alpha(finalHotseatAndPageIndicatorAlpha).withLayer();
-                pageIndicatorAlpha.addListener(new AlphaUpdateListener(pageIndicator,
-                        accessibilityEnabled));
-            } else {
-                // create a dummy animation so we don't need to do null checks later
-                pageIndicatorAlpha = ValueAnimator.ofFloat(0, 0);
-            }
-
-            LauncherViewPropertyAnimator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat)
-                    .alpha(finalHotseatAndPageIndicatorAlpha);
-            hotseatAlpha.addListener(new AlphaUpdateListener(hotseat, accessibilityEnabled));
+            Animator hotseatAlpha = mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha);
 
             LauncherViewPropertyAnimator overviewPanelAlpha =
                     new LauncherViewPropertyAnimator(overviewPanel).alpha(finalOverviewPanelAlpha);
             overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel,
                     accessibilityEnabled));
 
-            // For animation optimations, we may need to provide the Launcher transition
-            // with a set of views on which to force build layers in certain scenarios.
-            hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-            overviewPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-            if (layerViews != null) {
-                // If layerViews is not null, we add these views, and indicate that
-                // the caller can manage layer state.
-                layerViews.put(hotseat, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
-                layerViews.put(overviewPanel, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
-            } else {
-                // Otherwise let the animator handle layer management.
-                hotseatAlpha.withLayer();
-                overviewPanelAlpha.withLayer();
-            }
+            // For animation optimization, we may need to provide the Launcher transition
+            // with a set of views on which to force build and manage layers in certain scenarios.
+            layerViews.put(overviewPanel, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
+            layerViews.put(qsbContainer, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
+            layerViews.put(mLauncher.getHotseat(),
+                    LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
+            layerViews.put(mWorkspace.getPageIndicator(),
+                    LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
 
             if (states.workspaceToOverview) {
-                pageIndicatorAlpha.setInterpolator(new DecelerateInterpolator(2));
                 hotseatAlpha.setInterpolator(new DecelerateInterpolator(2));
                 overviewPanelAlpha.setInterpolator(null);
             } else if (states.overviewToWorkspace) {
-                pageIndicatorAlpha.setInterpolator(null);
                 hotseatAlpha.setInterpolator(null);
                 overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2));
             }
 
             overviewPanelAlpha.setDuration(duration);
-            pageIndicatorAlpha.setDuration(duration);
             hotseatAlpha.setDuration(duration);
+            qsbAlphaAnimation.setDuration(duration);
 
             mStateAnimator.play(overviewPanelAlpha);
             mStateAnimator.play(hotseatAlpha);
-            mStateAnimator.play(pageIndicatorAlpha);
+            mStateAnimator.play(qsbAlphaAnimation);
             mStateAnimator.addListener(new AnimatorListenerAdapter() {
+                boolean canceled = false;
+                @Override
+                public void onAnimationCancel(Animator animation) {
+                    canceled = true;
+                }
+
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     mStateAnimator = null;
-
+                    if (canceled) return;
                     if (accessibilityEnabled && overviewPanel.getVisibility() == View.VISIBLE) {
                         overviewPanel.getChildAt(0).performAccessibilityAction(
                                 AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
@@ -452,12 +438,9 @@
         } else {
             overviewPanel.setAlpha(finalOverviewPanelAlpha);
             AlphaUpdateListener.updateVisibility(overviewPanel, accessibilityEnabled);
-            hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha);
-            AlphaUpdateListener.updateVisibility(hotseat, accessibilityEnabled);
-            if (pageIndicator != null) {
-                pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha);
-                AlphaUpdateListener.updateVisibility(pageIndicator, accessibilityEnabled);
-            }
+
+            qsbAlphaAnimation.end();
+            mWorkspace.createHotseatAlphaAnimator(finalHotseatAlpha).end();
             mWorkspace.updateCustomContentVisibility();
             mWorkspace.setScaleX(mNewScale);
             mWorkspace.setScaleY(mNewScale);
@@ -482,14 +465,14 @@
 
         final DragLayer dragLayer = mLauncher.getDragLayer();
         final float startAlpha = dragLayer.getBackgroundAlpha();
-        float finalAlpha = states.stateIsNormal ? 0 : mWorkspaceScrimAlpha;
+        float finalAlpha = states.stateIsNormal || states.stateIsNormalHidden ?
+                0 : mWorkspaceScrimAlpha;
 
         if (finalAlpha != startAlpha) {
             if (animated) {
                 // These properties refer to the background protection gradient used for AllApps
                 // and Widget tray.
-                ValueAnimator bgFadeOutAnimation =
-                        LauncherAnimUtils.ofFloat(mWorkspace, startAlpha, finalAlpha);
+                ValueAnimator bgFadeOutAnimation = ValueAnimator.ofFloat(startAlpha, finalAlpha);
                 bgFadeOutAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                     @Override
                     public void onAnimationUpdate(ValueAnimator animation) {
diff --git a/src/com/android/launcher3/accessibility/AccessibileDragListenerAdapter.java b/src/com/android/launcher3/accessibility/AccessibileDragListenerAdapter.java
new file mode 100644
index 0000000..62a9a6d
--- /dev/null
+++ b/src/com/android/launcher3/accessibility/AccessibileDragListenerAdapter.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.accessibility;
+
+import android.view.ViewGroup;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.dragndrop.DragController.DragListener;
+import com.android.launcher3.dragndrop.DragOptions;
+
+/**
+ * Utility listener to enable/disable accessibility drag flags for a ViewGroup
+ * containing CellLayouts
+ */
+public class AccessibileDragListenerAdapter implements DragListener {
+
+    private final ViewGroup mViewGroup;
+    private final int mDragType;
+
+    /**
+     * @param parent
+     * @param dragType either {@link CellLayout#WORKSPACE_ACCESSIBILITY_DRAG} or
+     *                 {@link CellLayout#FOLDER_ACCESSIBILITY_DRAG}
+     */
+    public AccessibileDragListenerAdapter(ViewGroup parent, int dragType) {
+        mViewGroup = parent;
+        mDragType = dragType;
+    }
+
+    @Override
+    public void onDragStart(DragObject dragObject, DragOptions options) {
+        enableAccessibleDrag(true);
+    }
+
+    @Override
+    public void onDragEnd() {
+        enableAccessibleDrag(false);
+        Launcher.getLauncher(mViewGroup.getContext()).getDragController().removeDragListener(this);
+    }
+
+    protected void enableAccessibleDrag(boolean enable) {
+        for (int i = 0; i < mViewGroup.getChildCount(); i++) {
+            setEnableForLayout((CellLayout) mViewGroup.getChildAt(i), enable);
+        }
+    }
+
+    protected final void setEnableForLayout(CellLayout layout, boolean enable) {
+        layout.enableAccessibleDrag(enable, mDragType);
+    }
+}
diff --git a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
index 78accf7..bd3bb4d 100644
--- a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
@@ -26,7 +26,7 @@
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.launcher3.CellLayout;
-import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 
 import java.util.List;
@@ -50,7 +50,7 @@
         super(forView);
         mView = forView;
         mContext = mView.getContext();
-        mDelegate = LauncherAppState.getInstance().getAccessibilityDelegate();
+        mDelegate = Launcher.getLauncher(mContext).getAccessibilityDelegate();
     }
 
     @Override
diff --git a/src/com/android/launcher3/accessibility/DragViewStateAnnouncer.java b/src/com/android/launcher3/accessibility/DragViewStateAnnouncer.java
index 8ff82dd..99deb7b 100644
--- a/src/com/android/launcher3/accessibility/DragViewStateAnnouncer.java
+++ b/src/com/android/launcher3/accessibility/DragViewStateAnnouncer.java
@@ -21,6 +21,8 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 
+import com.android.launcher3.Launcher;
+
 /**
  * Periodically sends accessibility events to announce ongoing state changed. Based on the
  * implementation in ProgressBar.
@@ -50,6 +52,12 @@
         mTargetView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
     }
 
+    public void completeAction(int announceResId) {
+        cancel();
+        Launcher launcher = Launcher.getLauncher(mTargetView.getContext());
+        launcher.getDragLayer().announceForAccessibility(launcher.getText(announceResId));
+    }
+
     public static DragViewStateAnnouncer createFor(View v) {
         if (((AccessibilityManager) v.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE))
                 .isEnabled()) {
diff --git a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
index ff99890..d271f1d 100644
--- a/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/FolderAccessibilityHelper.java
@@ -17,7 +17,7 @@
 package com.android.launcher3.accessibility;
 
 import com.android.launcher3.CellLayout;
-import com.android.launcher3.FolderPagedView;
+import com.android.launcher3.folder.FolderPagedView;
 import com.android.launcher3.R;
 
 /**
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 2306b77..173aad0 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -18,11 +18,13 @@
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.AppWidgetResizeFrame;
+import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeleteDropTarget;
-import com.android.launcher3.DragController.DragListener;
 import com.android.launcher3.DragSource;
-import com.android.launcher3.Folder;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.folder.Folder;
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InfoDropTarget;
 import com.android.launcher3.ItemInfo;
@@ -36,6 +38,9 @@
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.UninstallDropTarget;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.dragndrop.DragController.DragListener;
+import com.android.launcher3.shortcuts.DeepShortcutTextView;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -45,13 +50,14 @@
 
     private static final String TAG = "LauncherAccessibilityDelegate";
 
-    private static final int REMOVE = R.id.action_remove;
-    private static final int INFO = R.id.action_info;
-    private static final int UNINSTALL = R.id.action_uninstall;
-    private static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
-    private static final int MOVE = R.id.action_move;
-    private static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
-    private static final int RESIZE = R.id.action_resize;
+    protected static final int REMOVE = R.id.action_remove;
+    protected static final int INFO = R.id.action_info;
+    protected static final int UNINSTALL = R.id.action_uninstall;
+    protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
+    protected static final int MOVE = R.id.action_move;
+    protected static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
+    protected static final int RESIZE = R.id.action_resize;
+    protected static final int DEEP_SHORTCUTS = R.id.action_deep_shortcuts;
 
     public enum DragType {
         ICON,
@@ -65,21 +71,20 @@
         public View item;
     }
 
-    private final SparseArray<AccessibilityAction> mActions = new SparseArray<>();
+    protected final SparseArray<AccessibilityAction> mActions = new SparseArray<>();
     @Thunk final Launcher mLauncher;
 
     private DragInfo mDragInfo = null;
-    private AccessibilityDragSource mDragSource = null;
 
     public LauncherAccessibilityDelegate(Launcher launcher) {
         mLauncher = launcher;
 
         mActions.put(REMOVE, new AccessibilityAction(REMOVE,
-                launcher.getText(R.string.delete_target_label)));
+                launcher.getText(R.string.remove_drop_target_label)));
         mActions.put(INFO, new AccessibilityAction(INFO,
-                launcher.getText(R.string.info_target_label)));
+                launcher.getText(R.string.app_info_drop_target_label)));
         mActions.put(UNINSTALL, new AccessibilityAction(UNINSTALL,
-                launcher.getText(R.string.delete_target_uninstall_label)));
+                launcher.getText(R.string.uninstall_drop_target_label)));
         mActions.put(ADD_TO_WORKSPACE, new AccessibilityAction(ADD_TO_WORKSPACE,
                 launcher.getText(R.string.action_add_to_workspace)));
         mActions.put(MOVE, new AccessibilityAction(MOVE,
@@ -88,21 +93,31 @@
                 launcher.getText(R.string.action_move_to_workspace)));
         mActions.put(RESIZE, new AccessibilityAction(RESIZE,
                         launcher.getText(R.string.action_resize)));
+        mActions.put(DEEP_SHORTCUTS, new AccessibilityAction(DEEP_SHORTCUTS,
+                launcher.getText(R.string.action_deep_shortcut)));
     }
 
     @Override
     public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfo(host, info);
+        addActions(host, info);
+    }
+
+    protected void addActions(View host, AccessibilityNodeInfo info) {
         if (!(host.getTag() instanceof ItemInfo)) return;
         ItemInfo item = (ItemInfo) host.getTag();
 
-        if (DeleteDropTarget.supportsDrop(item)) {
+        if (host instanceof BubbleTextView && ((BubbleTextView) host).hasDeepShortcuts()) {
+            info.addAction(mActions.get(DEEP_SHORTCUTS));
+        }
+
+        if (DeleteDropTarget.supportsAccessibleDrop(item)) {
             info.addAction(mActions.get(REMOVE));
         }
         if (UninstallDropTarget.supportsDrop(host.getContext(), item)) {
             info.addAction(mActions.get(UNINSTALL));
         }
-        if (InfoDropTarget.supportsDrop(host.getContext(), item)) {
+        if (InfoDropTarget.supportsDrop(item)) {
             info.addAction(mActions.get(INFO));
         }
 
@@ -118,7 +133,9 @@
                     info.addAction(mActions.get(RESIZE));
                 }
             }
-        } if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
+        }
+
+        if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
             info.addAction(mActions.get(ADD_TO_WORKSPACE));
         }
     }
@@ -137,7 +154,7 @@
             DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host);
             return true;
         } else if (action == INFO) {
-            InfoDropTarget.startDetailsActivityForInfo(item, mLauncher);
+            InfoDropTarget.startDetailsActivityForInfo(item, mLauncher, null);
             return true;
         } else if (action == UNINSTALL) {
             return UninstallDropTarget.startUninstallActivity(mLauncher, item);
@@ -174,7 +191,7 @@
             Folder folder = mLauncher.getWorkspace().getOpenFolder();
             mLauncher.closeFolder(folder, true);
             ShortcutInfo info = (ShortcutInfo) item;
-            folder.getInfo().remove(info);
+            folder.getInfo().remove(info, false);
 
             final int[] coordinates = new int[2];
             final long screenId = findSpaceOnWorkspace(item, coordinates);
@@ -213,6 +230,9 @@
                     }
                 })
                 .show();
+            return true;
+        } else if (action == DEEP_SHORTCUTS) {
+            return DeepShortcutsContainer.showForIcon((BubbleTextView) host) != null;
         }
         return false;
     }
@@ -353,26 +373,25 @@
 
         Folder folder = workspace.getOpenFolder();
         if (folder != null) {
-            if (folder.getItemsInReadingOrder().contains(item)) {
-                mDragSource = folder;
-            } else {
+            if (!folder.getItemsInReadingOrder().contains(item)) {
                 mLauncher.closeFolder();
+                folder = null;
             }
         }
-        if (mDragSource == null) {
-            mDragSource = workspace;
-        }
-        mDragSource.enableAccessibleDrag(true);
-        mDragSource.startDrag(cellInfo, true);
 
-        if (mLauncher.getDragController().isDragging()) {
-            mLauncher.getDragController().addDragListener(this);
+        mLauncher.getDragController().addDragListener(this);
+
+        DragOptions options = new DragOptions();
+        options.isAccessibleDrag = true;
+        if (folder != null) {
+            folder.startDrag(cellInfo.cell, options);
+        } else {
+            workspace.startDrag(cellInfo, options);
         }
     }
 
-
     @Override
-    public void onDragStart(DragSource source, Object info, int dragAction) {
+    public void onDragStart(DragObject dragObject, DragOptions options) {
         // No-op
     }
 
@@ -380,22 +399,12 @@
     public void onDragEnd() {
         mLauncher.getDragController().removeDragListener(this);
         mDragInfo = null;
-        if (mDragSource != null) {
-            mDragSource.enableAccessibleDrag(false);
-            mDragSource = null;
-        }
-    }
-
-    public static interface AccessibilityDragSource {
-        void startDrag(CellLayout.CellInfo cellInfo, boolean accessible);
-
-        void enableAccessibleDrag(boolean enable);
     }
 
     /**
      * Find empty space on the workspace and returns the screenId.
      */
-    private long findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) {
+    protected long findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) {
         Workspace workspace = mLauncher.getWorkspace();
         ArrayList<Long> workspaceScreens = workspace.getScreenOrder();
         long screenId;
diff --git a/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java
new file mode 100644
index 0000000..385a766
--- /dev/null
+++ b/src/com/android/launcher3/accessibility/OverviewAccessibilityDelegate.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.accessibility;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
+/**
+ * Accessibility delegate with actions pointing to various Overview entry points.
+ */
+public class OverviewAccessibilityDelegate extends AccessibilityDelegate {
+
+    private static final int OVERVIEW = R.string.accessibility_action_overview;
+    private static final int WALLPAPERS = R.string.wallpaper_button_text;
+    private static final int WIDGETS = R.string.widget_button_text;
+    private static final int SETTINGS = R.string.settings_button_text;
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(host, info);
+
+        Context context = host.getContext();
+        info.addAction(new AccessibilityAction(OVERVIEW, context.getText(OVERVIEW)));
+
+        if (Utilities.isWallapaperAllowed(context)) {
+            info.addAction(new AccessibilityAction(WALLPAPERS, context.getText(WALLPAPERS)));
+        }
+        info.addAction(new AccessibilityAction(WIDGETS, context.getText(WIDGETS)));
+        info.addAction(new AccessibilityAction(SETTINGS, context.getText(SETTINGS)));
+    }
+
+    @Override
+    public boolean performAccessibilityAction(View host, int action, Bundle args) {
+        Launcher launcher = Launcher.getLauncher(host.getContext());
+        if (action == OVERVIEW) {
+            launcher.showOverviewMode(true);
+            return true;
+        } else if (action == WALLPAPERS) {
+            launcher.onClickWallpaperPicker(host);
+            return true;
+        } else if (action == WIDGETS) {
+            launcher.onClickAddWidgetButton(host);
+            return true;
+        } else if (action == SETTINGS) {
+            launcher.onClickSettingsButton(host);
+            return true;
+        }
+        return super.performAccessibilityAction(host, action, args);
+    }
+}
diff --git a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
index c5b52de..5f68f90 100644
--- a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
@@ -28,6 +28,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.config.FeatureFlags;
 
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class OverviewScreenAccessibilityDelegate extends AccessibilityDelegate {
@@ -88,7 +89,9 @@
         if (index < mWorkspace.getChildCount() - 1) {
             info.addAction(mActions.get(MOVE_FORWARD));
         }
-        if (index > mWorkspace.numCustomPages()) {
+
+        int startIndex = mWorkspace.numCustomPages() + (FeatureFlags.QSB_ON_FIRST_SCREEN ? 1 : 0);
+        if (index > startIndex) {
             info.addAction(mActions.get(MOVE_BACKWARD));
         }
     }
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
new file mode 100644
index 0000000..0baa8f3
--- /dev/null
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.accessibility;
+
+import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+
+import java.util.ArrayList;
+
+/**
+ * Extension of {@link LauncherAccessibilityDelegate} with actions specific to shortcuts in
+ * deep shortcuts menu.
+ */
+public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDelegate {
+
+    public ShortcutMenuAccessibilityDelegate(Launcher launcher) {
+        super(launcher);
+    }
+
+    @Override
+    protected void addActions(View host, AccessibilityNodeInfo info) {
+        info.addAction(mActions.get(ADD_TO_WORKSPACE));
+    }
+
+    @Override
+    public boolean performAction(View host, ItemInfo item, int action) {
+        if (action == ADD_TO_WORKSPACE) {
+            if (!(host.getParent() instanceof DeepShortcutView)) {
+                return false;
+            }
+            final ShortcutInfo info = ((DeepShortcutView) host.getParent()).getFinalInfo();
+            final int[] coordinates = new int[2];
+            final long screenId = findSpaceOnWorkspace(item, coordinates);
+            Runnable onComplete = new Runnable() {
+                @Override
+                public void run() {
+                    LauncherModel.addItemToDatabase(mLauncher, info,
+                            LauncherSettings.Favorites.CONTAINER_DESKTOP,
+                            screenId, coordinates[0], coordinates[1]);
+                    ArrayList<ItemInfo> itemList = new ArrayList<>();
+                    itemList.add(info);
+                    mLauncher.bindItems(itemList, 0, itemList.size(), true);
+                    mLauncher.closeShortcutsContainer();
+                    announceConfirmation(R.string.item_added_to_workspace);
+                }
+            };
+
+            if (!mLauncher.showWorkspace(true, onComplete)) {
+                onComplete.run();
+            }
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index 73b824b..c71307d 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -17,6 +17,8 @@
 package com.android.launcher3.accessibility;
 
 import android.content.Context;
+import android.graphics.Rect;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
 import android.text.TextUtils;
 import android.view.View;
 
@@ -24,15 +26,20 @@
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.dragndrop.DragLayer;
 
 /**
  * Implementation of {@link DragAndDropAccessibilityDelegate} to support DnD on workspace.
  */
 public class WorkspaceAccessibilityHelper extends DragAndDropAccessibilityDelegate {
 
+    private final Rect mTempRect = new Rect();
+    private final int[] mTempCords = new int[2];
+
     public WorkspaceAccessibilityHelper(CellLayout layout) {
         super(layout);
     }
@@ -128,6 +135,25 @@
     }
 
     @Override
+    protected void onPopulateNodeForVirtualView(int id, AccessibilityNodeInfoCompat node) {
+        super.onPopulateNodeForVirtualView(id, node);
+
+
+        // ExploreByTouchHelper does not currently handle view scale.
+        // Update BoundsInScreen to appropriate value.
+        DragLayer dragLayer = Launcher.getLauncher(mView.getContext()).getDragLayer();
+        mTempCords[0] = mTempCords[1] = 0;
+        float scale = dragLayer.getDescendantCoordRelativeToSelf(mView, mTempCords);
+
+        node.getBoundsInParent(mTempRect);
+        mTempRect.left = mTempCords[0] + (int) (mTempRect.left * scale);
+        mTempRect.right = mTempCords[0] + (int) (mTempRect.right * scale);
+        mTempRect.top = mTempCords[1] + (int) (mTempRect.top * scale);
+        mTempRect.bottom = mTempCords[1] + (int) (mTempRect.bottom * scale);
+        node.setBoundsInScreen(mTempRect);
+    }
+
+    @Override
     protected String getLocationDescriptionForIconDrop(int id) {
         int x = id % mView.getCountX();
         int y = id / mView.getCountX();
diff --git a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
index dafa73f..cfd07e6 100644
--- a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
+++ b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
@@ -96,14 +96,14 @@
     public AllAppsBackgroundDrawable(Context context) {
         Resources res = context.getResources();
         mHand = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_hand,
-                0.575f, 0.1f, Gravity.CENTER_HORIZONTAL);
+                0.575f, 0.f, Gravity.CENTER_HORIZONTAL);
         mIcons = new TransformedImageDrawable[4];
         mIcons[0] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_1,
                 0.375f, 0, Gravity.CENTER_HORIZONTAL);
         mIcons[1] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_2,
-                0.3125f, 0.25f, Gravity.CENTER_HORIZONTAL);
+                0.3125f, 0.2f, Gravity.CENTER_HORIZONTAL);
         mIcons[2] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_3,
-                0.475f, 0.4f, Gravity.CENTER_HORIZONTAL);
+                0.475f, 0.26f, Gravity.CENTER_HORIZONTAL);
         mIcons[3] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_4,
                 0.7f, 0.125f, Gravity.CENTER_HORIZONTAL);
         mWidth = res.getDimensionPixelSize(R.dimen.all_apps_background_canvas_width);
diff --git a/src/com/android/launcher3/allapps/AllAppsCaretController.java b/src/com/android/launcher3/allapps/AllAppsCaretController.java
new file mode 100644
index 0000000..622322b
--- /dev/null
+++ b/src/com/android/launcher3/allapps/AllAppsCaretController.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps;
+
+import android.animation.ObjectAnimator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.pageindicators.CaretDrawable;
+
+public class AllAppsCaretController {
+    // Determines when the caret should flip. Should be accessed via getThreshold()
+    private static final float CARET_THRESHOLD = 0.015f;
+    private static final float CARET_THRESHOLD_LAND = 0.5f;
+    // The velocity at which the caret will peak (i.e. exhibit a 90 degree bend)
+    private static final float PEAK_VELOCITY = VerticalPullDetector.RELEASE_VELOCITY_PX_MS * .7f;
+
+    private Launcher mLauncher;
+
+    private ObjectAnimator mCaretAnimator;
+    private CaretDrawable mCaretDrawable;
+    private float mLastCaretProgress;
+    private boolean mThresholdCrossed;
+
+    public AllAppsCaretController(CaretDrawable caret, Launcher launcher) {
+        mLauncher = launcher;
+        mCaretDrawable = caret;
+
+        final long caretAnimationDuration = launcher.getResources().getInteger(
+                R.integer.config_caretAnimationDuration);
+        final Interpolator caretInterpolator = AnimationUtils.loadInterpolator(launcher,
+                android.R.interpolator.fast_out_slow_in);
+
+        // We will set values later
+        mCaretAnimator = ObjectAnimator.ofFloat(mCaretDrawable, "caretProgress", 0);
+        mCaretAnimator.setDuration(caretAnimationDuration);
+        mCaretAnimator.setInterpolator(caretInterpolator);
+    }
+
+    /**
+     * Updates the state of the caret based on the progress of the {@link AllAppsContainerView}, as
+     * defined by the {@link AllAppsTransitionController}. Uses the container's velocity to adjust
+     * angle of caret.
+     *
+     * @param containerProgress The progress of the container in the range [0..1]
+     * @param velocity The velocity of the container
+     * @param dragging {@code true} if the container is being dragged
+     */
+    public void updateCaret(float containerProgress, float velocity, boolean dragging) {
+        // If we're in portrait and the shift is not 0 or 1, adjust the caret based on velocity
+        if (getThreshold() < containerProgress && containerProgress < 1 - getThreshold() &&
+                !mLauncher.useVerticalBarLayout()) {
+            mThresholdCrossed = true;
+
+            // How fast are we moving as a percentage of the peak velocity?
+            final float pctOfFlingVelocity = Math.max(-1, Math.min(velocity / PEAK_VELOCITY, 1));
+
+            mCaretDrawable.setCaretProgress(pctOfFlingVelocity);
+
+            // Set the last caret progress to this progress to prevent animator cancellation
+            mLastCaretProgress = pctOfFlingVelocity;
+            // Animate to neutral. This is necessary so the caret doesn't "freeze" when the
+            // container stops moving (e.g., during a drag or when the threshold is reached).
+            animateCaretToProgress(CaretDrawable.PROGRESS_CARET_NEUTRAL);
+        } else if (!dragging) {
+            // Otherwise, if we're not dragging, match the caret to the appropriate state
+            if (containerProgress <= getThreshold()) { // All Apps is up
+                animateCaretToProgress(CaretDrawable.PROGRESS_CARET_POINTING_DOWN);
+            } else if (containerProgress >= 1 - getThreshold()) { // All Apps is down
+                animateCaretToProgress(CaretDrawable.PROGRESS_CARET_POINTING_UP);
+            }
+        }
+    }
+
+    private void animateCaretToProgress(float progress) {
+        // If the new progress is the same as the last progress we animated to, terminate early
+        if (Float.compare(mLastCaretProgress, progress) == 0) {
+            return;
+        }
+
+        if (mCaretAnimator.isRunning()) {
+            mCaretAnimator.cancel(); // Stop the animator in its tracks
+        }
+
+        // Update the progress and start the animation
+        mLastCaretProgress = progress;
+        mCaretAnimator.setFloatValues(progress);
+        mCaretAnimator.start();
+    }
+
+    private float getThreshold() {
+        // In landscape, just return the landscape threshold.
+        if (mLauncher.useVerticalBarLayout()) {
+            return CARET_THRESHOLD_LAND;
+        }
+
+        // Before the threshold is crossed, it is reported as zero. This makes the caret immediately
+        // responsive when a drag begins. After the threshold is crossed, we return the constant
+        // value. This means the caret will start its state-based adjustment sooner. That is, we
+        // won't have to wait until the panel is completely settled to begin animation.
+        return mThresholdCrossed ? CARET_THRESHOLD : 0f;
+    }
+
+    public void onDragStart() {
+        mThresholdCrossed = false;
+    }
+}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index c12f645..77ef642 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -22,14 +22,17 @@
 import android.graphics.Rect;
 import android.support.v7.widget.RecyclerView;
 import android.text.Selection;
+import android.text.Spannable;
+import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
 import android.text.method.TextKeyListener;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.ViewGroup;
 
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.BaseContainerView;
@@ -40,13 +43,19 @@
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.ExtendedEditText;
-import com.android.launcher3.Folder;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherTransitionable;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.graphics.TintedDrawableSpan;
+import com.android.launcher3.keyboard.FocusedItemDecorator;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.ComponentKey;
 
 import java.nio.charset.Charset;
@@ -55,7 +64,6 @@
 import java.util.List;
 
 
-
 /**
  * A merge algorithm that merges every section indiscriminately.
  */
@@ -63,10 +71,10 @@
 
     @Override
     public boolean continueMerging(AlphabeticalAppsList.SectionInfo section,
-           AlphabeticalAppsList.SectionInfo withSection,
-           int sectionAppCount, int numAppsPerRow, int mergeCount) {
+            AlphabeticalAppsList.SectionInfo withSection,
+            int sectionAppCount, int numAppsPerRow, int mergeCount) {
         // Don't merge the predicted apps
-        if (section.firstAppItem.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
+        if (section.firstAppItem.viewType != AllAppsGridAdapter.VIEW_TYPE_ICON) {
             return false;
         }
         // Otherwise, merge every other section
@@ -95,10 +103,10 @@
 
     @Override
     public boolean continueMerging(AlphabeticalAppsList.SectionInfo section,
-           AlphabeticalAppsList.SectionInfo withSection,
-           int sectionAppCount, int numAppsPerRow, int mergeCount) {
+            AlphabeticalAppsList.SectionInfo withSection,
+            int sectionAppCount, int numAppsPerRow, int mergeCount) {
         // Don't merge the predicted apps
-        if (section.firstAppItem.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
+        if (section.firstAppItem.viewType != AllAppsGridAdapter.VIEW_TYPE_ICON) {
             return false;
         }
 
@@ -126,8 +134,7 @@
  * The all apps view container.
  */
 public class AllAppsContainerView extends BaseContainerView implements DragSource,
-        LauncherTransitionable, View.OnTouchListener, View.OnLongClickListener,
-        AllAppsSearchBarController.Callbacks {
+        LauncherTransitionable, View.OnLongClickListener, AllAppsSearchBarController.Callbacks {
 
     private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;
     private static final int MAX_NUM_MERGES_PHONE = 2;
@@ -147,17 +154,16 @@
     private View mSearchContainer;
     private ExtendedEditText mSearchInput;
     private HeaderElevationController mElevationController;
+    private int mSearchContainerOffsetTop;
 
     private SpannableStringBuilder mSearchQueryBuilder = null;
 
     private int mSectionNamesMargin;
     private int mNumAppsPerRow;
     private int mNumPredictedAppsPerRow;
-    private int mRecyclerViewTopBottomPadding;
+    private int mRecyclerViewBottomPadding;
     // This coordinate is relative to this container view
     private final Point mBoundsCheckLastTouchDownPos = new Point(-1, -1);
-    // This coordinate is relative to its parent
-    private final Point mIconLastTouchPos = new Point();
 
     public AllAppsContainerView(Context context) {
         this(context, null);
@@ -171,16 +177,21 @@
         super(context, attrs, defStyleAttr);
         Resources res = context.getResources();
 
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
         mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
         mApps = new AlphabeticalAppsList(context);
-        mAdapter = new AllAppsGridAdapter(mLauncher, mApps, this, mLauncher, this);
+        mAdapter = new AllAppsGridAdapter(mLauncher, mApps, mLauncher, this);
         mApps.setAdapter(mAdapter);
         mLayoutManager = mAdapter.getLayoutManager();
         mItemDecoration = mAdapter.getItemDecoration();
-        mRecyclerViewTopBottomPadding =
-                res.getDimensionPixelSize(R.dimen.all_apps_list_top_bottom_padding);
-
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && !grid.isVerticalBarLayout()) {
+            mRecyclerViewBottomPadding = 0;
+            setPadding(0, 0, 0, 0);
+        } else {
+            mRecyclerViewBottomPadding =
+                    res.getDimensionPixelSize(R.dimen.all_apps_list_bottom_padding);
+        }
         mSearchQueryBuilder = new SpannableStringBuilder();
         Selection.setSelection(mSearchQueryBuilder, 0);
     }
@@ -204,6 +215,7 @@
      */
     public void addApps(List<AppInfo> apps) {
         mApps.addApps(apps);
+        mSearchBarController.refreshSearchResult();
     }
 
     /**
@@ -211,6 +223,7 @@
      */
     public void updateApps(List<AppInfo> apps) {
         mApps.updateApps(apps);
+        mSearchBarController.refreshSearchResult();
     }
 
     /**
@@ -218,6 +231,15 @@
      */
     public void removeApps(List<AppInfo> apps) {
         mApps.removeApps(apps);
+        mSearchBarController.refreshSearchResult();
+    }
+
+    public void setSearchBarVisible(boolean visible) {
+        if (visible) {
+            mSearchBarController.setVisibility(View.VISIBLE);
+        } else {
+            mSearchBarController.setVisibility(View.INVISIBLE);
+        }
     }
 
     /**
@@ -230,8 +252,6 @@
         mSearchBarController = searchController;
         mSearchBarController.initialize(mApps, mSearchInput, mLauncher, this);
         mAdapter.setSearchController(mSearchBarController);
-
-        updateBackgroundAndPaddings();
     }
 
     /**
@@ -242,6 +262,39 @@
     }
 
     /**
+     * Returns whether the view itself will handle the touch event or not.
+     */
+    public boolean shouldContainerScroll(MotionEvent ev) {
+        int[] point = new int[2];
+        point[0] = (int) ev.getX();
+        point[1] = (int) ev.getY();
+        Utilities.mapCoordInSelfToDescendent(mAppsRecyclerView, this, point);
+
+        // IF the MotionEvent is inside the search box, and the container keeps on receiving
+        // touch input, container should move down.
+        if (mLauncher.getDragLayer().isEventOverView(mSearchContainer, ev)) {
+            return true;
+        }
+
+        // IF the MotionEvent is inside the thumb, container should not be pulled down.
+        if (mAppsRecyclerView.getScrollBar().isNearThumb(point[0], point[1])) {
+            return false;
+        }
+
+        // IF a shortcuts container is open, container should not be pulled down.
+        if (mLauncher.getOpenShortcutsContainer() != null) {
+            return false;
+        }
+
+        // IF scroller is at the very top OR there is no scroll bar because there is probably not
+        // enough items to scroll, THEN it's okay for the container to be pulled down.
+        if (mAppsRecyclerView.getScrollBar().getThumbOffset().y <= 0) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Focuses the search field and begins an app search.
      */
     public void startAppsSearch() {
@@ -255,6 +308,7 @@
      */
     public void reset() {
         // Reset the search bar and base recycler view after transitioning home
+        scrollToTop();
         mSearchBarController.reset();
         mAppsRecyclerView.reset();
     }
@@ -276,6 +330,19 @@
 
         mSearchContainer = findViewById(R.id.search_container);
         mSearchInput = (ExtendedEditText) findViewById(R.id.search_box_input);
+
+        // Update the hint to contain the icon.
+        // Prefix the original hint with two spaces. The first space gets replaced by the icon
+        // using span. The second space is used for a singe space character between the hint
+        // and the icon.
+        SpannableString spanned = new SpannableString("  " + mSearchInput.getHint());
+        spanned.setSpan(new TintedDrawableSpan(getContext(), R.drawable.ic_allapps_search),
+                0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+        mSearchInput.setHint(spanned);
+
+        mSearchContainerOffsetTop = getResources().getDimensionPixelSize(
+                R.dimen.all_apps_search_bar_margin_top);
+
         mElevationController = Utilities.ATLEAST_LOLLIPOP
                 ? new HeaderElevationController.ControllerVL(mSearchContainer)
                 : new HeaderElevationController.ControllerV16(mSearchContainer);
@@ -293,46 +360,61 @@
             mAppsRecyclerView.addItemDecoration(mItemDecoration);
         }
 
-        // Precalculate the prediction icon and normal icon sizes
-        LayoutInflater layoutInflater = LayoutInflater.from(getContext());
-        final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
-                getResources().getDisplayMetrics().widthPixels, MeasureSpec.AT_MOST);
-        final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(
-                getResources().getDisplayMetrics().heightPixels, MeasureSpec.AT_MOST);
+        FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(mAppsRecyclerView);
+        mAppsRecyclerView.addItemDecoration(focusedItemDecorator);
+        mAppsRecyclerView.preMeasureViews(mAdapter);
+        mAdapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
 
-        BubbleTextView icon = (BubbleTextView) layoutInflater.inflate(
-                R.layout.all_apps_icon, this, false);
-        icon.applyDummyInfo();
-        icon.measure(widthMeasureSpec, heightMeasureSpec);
-        BubbleTextView predIcon = (BubbleTextView) layoutInflater.inflate(
-                R.layout.all_apps_prediction_bar_icon, this, false);
-        predIcon.applyDummyInfo();
-        predIcon.measure(widthMeasureSpec, heightMeasureSpec);
-        mAppsRecyclerView.setPremeasuredIconHeights(predIcon.getMeasuredHeight(),
-                icon.getMeasuredHeight());
-
-        updateBackgroundAndPaddings();
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+            getRevealView().setVisibility(View.VISIBLE);
+            getContentView().setVisibility(View.VISIBLE);
+            getContentView().setBackground(null);
+        }
     }
 
     @Override
-    public void onBoundsChanged(Rect newBounds) {
-        mLauncher.updateOverlayBounds(newBounds);
-    }
+    public void onBoundsChanged(Rect newBounds) { }
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        mContentBounds.set(mContentPadding.left, mContentPadding.top,
-                MeasureSpec.getSize(widthMeasureSpec) - mContentPadding.right,
-                MeasureSpec.getSize(heightMeasureSpec) - mContentPadding.bottom);
+        int widthPx = MeasureSpec.getSize(widthMeasureSpec);
+        int heightPx = MeasureSpec.getSize(heightMeasureSpec);
+        updatePaddingsAndMargins(widthPx, heightPx);
+        mContentBounds.set(mContainerPaddingLeft, 0, widthPx - mContainerPaddingRight, heightPx);
+
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        grid.updateAppsViewNumCols();
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+            if (mNumAppsPerRow != grid.inv.numColumns ||
+                    mNumPredictedAppsPerRow != grid.inv.numColumns) {
+                mNumAppsPerRow = grid.inv.numColumns;
+                mNumPredictedAppsPerRow = grid.inv.numColumns;
+
+                mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
+                mAdapter.setNumAppsPerRow(mNumAppsPerRow);
+                mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, new FullMergeAlgorithm());
+                if (mNumAppsPerRow > 0) {
+                    int rvPadding = mAppsRecyclerView.getPaddingStart(); // Assumes symmetry
+                    final int thumbMaxWidth =
+                            getResources().getDimensionPixelSize(
+                                    R.dimen.container_fastscroll_thumb_max_width);
+                    mSearchContainer.setPadding(
+                            rvPadding - mContainerPaddingLeft + thumbMaxWidth,
+                            mSearchContainer.getPaddingTop(),
+                            rvPadding - mContainerPaddingRight + thumbMaxWidth,
+                            mSearchContainer.getPaddingBottom());
+                }
+            }
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            return;
+        }
+
+        // --- remove START when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
 
         // Update the number of items in the grid before we measure the view
         // TODO: mSectionNamesMargin is currently 0, but also account for it,
         // if it's enabled in the future.
-        int availableWidth = (!mContentBounds.isEmpty() ? mContentBounds.width() :
-                MeasureSpec.getSize(widthMeasureSpec))
-                    - 2 * mAppsRecyclerView.getMaxScrollbarWidth();
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        grid.updateAppsViewNumCols(getResources(), availableWidth);
+        grid.updateAppsViewNumCols();
         if (mNumAppsPerRow != grid.allAppsNumCols ||
                 mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) {
             mNumAppsPerRow = grid.allAppsNumCols;
@@ -349,14 +431,9 @@
             mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
             mAdapter.setNumAppsPerRow(mNumAppsPerRow);
             mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm);
-
-            if (mNumAppsPerRow > 0) {
-                int iconSize = availableWidth / mNumAppsPerRow;
-                int iconSpacing = (iconSize - grid.allAppsIconSizePx) / 2;
-                mSearchInput.setPaddingRelative(iconSpacing, 0, iconSpacing, 0);
-            }
         }
 
+        // --- remove END when {@code FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP} is enabled. ---
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
@@ -365,8 +442,10 @@
      * container view, we inset the background and padding of the recycler view to allow for the
      * recycler view to handle touch events (for fast scrolling) all the way to the edge.
      */
-    @Override
-    protected void onUpdateBgPadding(Rect padding, Rect bgPadding) {
+    private void updatePaddingsAndMargins(int widthPx, int heightPx) {
+        Rect bgPadding = new Rect();
+        getRevealView().getBackground().getPadding(bgPadding);
+
         mAppsRecyclerView.updateBackgroundPadding(bgPadding);
         mAdapter.updateBackgroundPadding(bgPadding);
         mElevationController.updateBackgroundPadding(bgPadding);
@@ -375,18 +454,53 @@
         // names)
         int maxScrollBarWidth = mAppsRecyclerView.getMaxScrollbarWidth();
         int startInset = Math.max(mSectionNamesMargin, maxScrollBarWidth);
-        int topBottomPadding = mRecyclerViewTopBottomPadding;
         if (Utilities.isRtl(getResources())) {
-            mAppsRecyclerView.setPadding(padding.left + maxScrollBarWidth,
-                    topBottomPadding, padding.right + startInset, topBottomPadding);
+            mAppsRecyclerView.setPadding(bgPadding.left + maxScrollBarWidth, 0, bgPadding.right
+                    + startInset, mRecyclerViewBottomPadding);
         } else {
-            mAppsRecyclerView.setPadding(padding.left + startInset, topBottomPadding,
-                    padding.right + maxScrollBarWidth, topBottomPadding);
+            mAppsRecyclerView.setPadding(bgPadding.left + startInset, 0, bgPadding.right +
+                    maxScrollBarWidth, mRecyclerViewBottomPadding);
         }
 
         MarginLayoutParams lp = (MarginLayoutParams) mSearchContainer.getLayoutParams();
-        lp.leftMargin = padding.left;
-        lp.rightMargin = padding.right;
+        lp.leftMargin = bgPadding.left;
+        lp.rightMargin = bgPadding.right;
+
+        // Clip the view to the left and right edge of the background to
+        // to prevent shadows from rendering beyond the edges
+        final Rect newClipBounds = new Rect(
+                bgPadding.left, 0, widthPx - bgPadding.right, heightPx);
+        setClipBounds(newClipBounds);
+
+        // Allow the overscroll effect to reach the edges of the view
+        mAppsRecyclerView.setClipToPadding(false);
+
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+            if (!grid.isVerticalBarLayout()) {
+                MarginLayoutParams mlp = (MarginLayoutParams) mAppsRecyclerView.getLayoutParams();
+
+                Rect insets = mLauncher.getDragLayer().getInsets();
+                getContentView().setPadding(0, 0, 0, 0);
+                int height = insets.top + grid.hotseatCellHeightPx;
+
+                mlp.topMargin = height;
+                mAppsRecyclerView.setLayoutParams(mlp);
+
+                mSearchContainer.setPadding(
+                        mSearchContainer.getPaddingLeft(),
+                        insets.top + mSearchContainerOffsetTop,
+                        mSearchContainer.getPaddingRight(),
+                        mSearchContainer.getPaddingBottom());
+                lp.height = height;
+
+                View navBarBg = findViewById(R.id.nav_bar_bg);
+                ViewGroup.LayoutParams params = navBarBg.getLayoutParams();
+                params.height = insets.bottom;
+                navBarBg.setLayoutParams(params);
+                navBarBg.setVisibility(View.VISIBLE);
+            }
+        }
         mSearchContainer.setLayoutParams(lp);
     }
 
@@ -422,32 +536,40 @@
         return handleTouchEvent(ev);
     }
 
-    @SuppressLint("ClickableViewAccessibility")
-    @Override
-    public boolean onTouch(View v, MotionEvent ev) {
-        switch (ev.getAction()) {
-            case MotionEvent.ACTION_DOWN:
-            case MotionEvent.ACTION_MOVE:
-                mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY());
-                break;
-        }
-        return false;
-    }
-
     @Override
     public boolean onLongClick(View v) {
         // Return early if this is not initiated from a touch
         if (!v.isInTouchMode()) return false;
         // When we have exited all apps or are in transition, disregard long clicks
+
         if (!mLauncher.isAppsViewVisible() ||
                 mLauncher.getWorkspace().isSwitchingState()) return false;
-        // Return if global dragging is not enabled
+        // Return if global dragging is not enabled or we are already dragging
         if (!mLauncher.isDraggingEnabled()) return false;
+        if (mLauncher.getDragController().isDragging()) return false;
 
         // Start the drag
-        mLauncher.getWorkspace().beginDragShared(v, mIconLastTouchPos, this, false);
-        // Enter spring loaded mode
-        mLauncher.enterSpringLoadedDragMode();
+        DragOptions dragOptions = new DragOptions();
+        if (v instanceof BubbleTextView) {
+            final BubbleTextView icon = (BubbleTextView) v;
+            if (icon.hasDeepShortcuts()) {
+                DeepShortcutsContainer dsc = DeepShortcutsContainer.showForIcon(icon);
+                if (dsc != null) {
+                    dragOptions.deferDragCondition = dsc.createDeferDragCondition(new Runnable() {
+                        @Override
+                        public void run() {
+                            icon.setVisibility(VISIBLE);
+                        }
+                    });
+                }
+            }
+        }
+        mLauncher.getWorkspace().beginDragShared(v, this, dragOptions);
+        if (FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
+            // Enter spring loaded mode (the new workspace does this in
+            // onDragStart(), so we don't want to do it here)
+            mLauncher.enterSpringLoadedDragMode();
+        }
 
         return false;
     }
@@ -497,11 +619,11 @@
         // target layout we were dropping on.
         if (!success) {
             boolean showOutOfSpaceMessage = false;
-            if (target instanceof Workspace) {
+            if (target instanceof Workspace && !mLauncher.getDragController().isDeferringDrag()) {
                 int currentScreen = mLauncher.getCurrentWorkspaceScreen();
                 Workspace workspace = (Workspace) target;
                 CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
-                ItemInfo itemInfo = (ItemInfo) d.dragInfo;
+                ItemInfo itemInfo = d.dragInfo;
                 if (layout != null) {
                     showOutOfSpaceMessage =
                             !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
@@ -516,7 +638,8 @@
     }
 
     @Override
-    public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
+    public void onLauncherTransitionPrepare(Launcher l, boolean animated,
+            boolean multiplePagesVisible) {
         // Do nothing
     }
 
@@ -573,7 +696,7 @@
                     float distance = (float) Math.hypot(dx, dy);
                     if (distance < viewConfig.getScaledTouchSlop()) {
                         // The background was clicked, so just go home
-                        Launcher launcher = (Launcher) getContext();
+                        Launcher launcher = Launcher.getLauncher(getContext());
                         launcher.showWorkspace(true);
                         return true;
                     }
@@ -607,4 +730,13 @@
         mSearchQueryBuilder.clearSpans();
         Selection.setSelection(mSearchQueryBuilder, 0);
     }
+
+    @Override
+    public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
+        targetParent.containerType = mAppsRecyclerView.getContainerType(v);
+    }
+
+    public boolean shouldRestoreImeState() {
+        return !TextUtils.isEmpty(mSearchInput.getText());
+    }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
index 73de45e..76934af 100644
--- a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
+++ b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
@@ -18,12 +18,12 @@
 import android.support.v7.widget.RecyclerView;
 import android.view.View;
 
-import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.BaseRecyclerViewFastScrollBar;
 import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.util.Thunk;
 
 import java.util.HashSet;
+import java.util.List;
 
 public class AllAppsFastScrollHelper implements AllAppsGridAdapter.BindViewCallback {
 
@@ -143,13 +143,22 @@
         }
 
         // Calculate the full animation from the current scroll position to the final scroll
-        // position, and then run the animation for the duration.
-        int newScrollY = Math.min(availableScrollHeight,
-                mRv.getPaddingTop() + mRv.getTop(info.fastScrollToItem.rowIndex));
+        // position, and then run the animation for the duration.  If we are scrolling to the
+        // first fast scroll section, then just scroll to the top of the list itself.
+        List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections =
+                mApps.getFastScrollerSections();
+        int newPosition = info.fastScrollToItem.position;
+        int newScrollY = fastScrollSections.size() > 0 && fastScrollSections.get(0) == info
+                        ? 0
+                        : Math.min(availableScrollHeight, mRv.getCurrentScrollY(newPosition, 0));
         int numFrames = mFastScrollFrames.length;
+        int deltaY = newScrollY - scrollY;
+        float ySign = Math.signum(deltaY);
+        int step = (int) (ySign * Math.ceil((float) Math.abs(deltaY) / numFrames));
         for (int i = 0; i < numFrames; i++) {
             // TODO(winsonc): We can interpolate this as well.
-            mFastScrollFrames[i] = (newScrollY - scrollY) / numFrames;
+            mFastScrollFrames[i] = (int) (ySign * Math.min(Math.abs(step), Math.abs(deltaY)));
+            deltaY -= step;
         }
         mFastScrollFrameIndex = 0;
         mRv.postOnAnimation(mSmoothSnapNextFrameRunnable);
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 0460c91..7b6aef1 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -17,32 +17,28 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Paint;
+import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat;
-import android.support.v4.view.accessibility.AccessibilityRecordCompat;
 import android.support.v4.view.accessibility.AccessibilityEventCompat;
-import android.net.Uri;
+import android.support.v4.view.accessibility.AccessibilityRecordCompat;
 import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.RecyclerView.Recycler;
-import android.support.v7.widget.RecyclerView.State;
-import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnFocusChangeListener;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.TextView;
+
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -50,7 +46,6 @@
 import java.util.HashMap;
 import java.util.List;
 
-
 /**
  * The grid view adapter of all the apps.
  */
@@ -60,17 +55,34 @@
     private static final boolean DEBUG = false;
 
     // A section break in the grid
-    public static final int SECTION_BREAK_VIEW_TYPE = 0;
+    public static final int VIEW_TYPE_SECTION_BREAK = 1 << 0;
     // A normal icon
-    public static final int ICON_VIEW_TYPE = 1;
+    public static final int VIEW_TYPE_ICON = 1 << 1;
     // A prediction icon
-    public static final int PREDICTION_ICON_VIEW_TYPE = 2;
+    public static final int VIEW_TYPE_PREDICTION_ICON = 1 << 2;
     // The message shown when there are no filtered results
-    public static final int EMPTY_SEARCH_VIEW_TYPE = 3;
-    // A divider that separates the apps list and the search market button
-    public static final int SEARCH_MARKET_DIVIDER_VIEW_TYPE = 4;
+    public static final int VIEW_TYPE_EMPTY_SEARCH = 1 << 3;
     // The message to continue to a market search when there are no filtered results
-    public static final int SEARCH_MARKET_VIEW_TYPE = 5;
+    public static final int VIEW_TYPE_SEARCH_MARKET = 1 << 4;
+
+    // We use various dividers for various purposes.  They share enough attributes to reuse layouts,
+    // but differ in enough attributes to require different view types
+
+    // A divider that separates the apps list and the search market button
+    public static final int VIEW_TYPE_SEARCH_MARKET_DIVIDER = 1 << 5;
+    // The divider under the search field
+    public static final int VIEW_TYPE_SEARCH_DIVIDER = 1 << 6;
+    // The divider that separates prediction icons from the app list
+    public static final int VIEW_TYPE_PREDICTION_DIVIDER = 1 << 7;
+
+    // Common view type masks
+    public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_SEARCH_DIVIDER
+            | VIEW_TYPE_SEARCH_MARKET_DIVIDER
+            | VIEW_TYPE_PREDICTION_DIVIDER
+            | VIEW_TYPE_SECTION_BREAK;
+    public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON
+            | VIEW_TYPE_PREDICTION_ICON;
+
 
     public interface BindViewCallback {
         public void onBindView(ViewHolder holder);
@@ -105,67 +117,23 @@
             // adapter views
             final AccessibilityRecordCompat record = AccessibilityEventCompat
                     .asRecord(event);
-
-            // count the number of SECTION_BREAK_VIEW_TYPE that is wrongfully
-            // initialized as a node (also a row) for talk back.
-            int numEmptyNode = getEmptyRowForAccessibility(-1 /* no view type */);
-            record.setFromIndex(event.getFromIndex() - numEmptyNode);
-            record.setToIndex(event.getToIndex() - numEmptyNode);
             record.setItemCount(mApps.getNumFilteredApps());
         }
 
         @Override
-        public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler,
-                State state, View host, AccessibilityNodeInfoCompat info) {
-
-            int viewType = getItemViewType(host);
-            // Only initialize on node that is meaningful. Subtract empty row count.
-            if (viewType == ICON_VIEW_TYPE || viewType == PREDICTION_ICON_VIEW_TYPE) {
-                super.onInitializeAccessibilityNodeInfoForItem(recycler, state, host, info);
-                CollectionItemInfoCompat itemInfo = info.getCollectionItemInfo();
-                final CollectionItemInfoCompat dstItemInfo = CollectionItemInfoCompat.obtain(
-                        itemInfo.getRowIndex() - getEmptyRowForAccessibility(viewType),
-                        itemInfo.getRowSpan(),
-                        itemInfo.getColumnIndex(),
-                        itemInfo.getColumnSpan(),
-                        itemInfo.isHeading(),
-                        itemInfo.isSelected());
-                info.setCollectionItemInfo(dstItemInfo);
-            }
-        }
-
-        @Override
         public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
                 RecyclerView.State state) {
-            return super.getRowCountForAccessibility(recycler, state)
-                    - getEmptyRowForAccessibility(-1 /* no view type */);
+            if (mApps.hasNoFilteredResults()) {
+                // Disregard the no-search-results text as a list item for accessibility
+                return 0;
+            } else {
+                return super.getRowCountForAccessibility(recycler, state);
+            }
         }
 
-        /**
-         * Returns the total number of SECTION_BREAK_VIEW_TYPE that is wrongfully
-         * initialized as a node (also a row) for talk back.
-         */
-        private int getEmptyRowForAccessibility(int viewType) {
-            int numEmptyNode = 0;
-            if (mApps.hasFilter()) {
-                // search result screen has only one SECTION_BREAK_VIEW
-                numEmptyNode = 1;
-            } else {
-                // default all apps screen may have one or two SECTION_BREAK_VIEW
-                numEmptyNode = 1;
-                if (mApps.hasPredictedComponents()) {
-                    if (viewType == PREDICTION_ICON_VIEW_TYPE) {
-                        numEmptyNode = 1;
-                    } else if (viewType == ICON_VIEW_TYPE) {
-                        numEmptyNode = 2;
-                    }
-                } else {
-                    if (viewType == ICON_VIEW_TYPE) {
-                        numEmptyNode = 1;
-                    }
-                }
-            }
-            return numEmptyNode;
+        @Override
+        public int getPaddingBottom() {
+            return mLauncher.getDragLayer().getInsets().bottom;
         }
     }
 
@@ -181,11 +149,9 @@
 
         @Override
         public int getSpanSize(int position) {
-            switch (mApps.getAdapterItems().get(position).viewType) {
-                case AllAppsGridAdapter.ICON_VIEW_TYPE:
-                case AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE:
-                    return 1;
-                default:
+            if (isIconViewType(mApps.getAdapterItems().get(position).viewType)) {
+                return 1;
+            } else {
                     // Section breaks span the full width
                     return mAppsPerRow;
             }
@@ -217,7 +183,6 @@
             }
 
             List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
-            boolean hasDrawnPredictedAppsDivider = false;
             boolean showSectionNames = mSectionNamesMargin > 0;
             int childCount = parent.getChildCount();
             int lastSectionTop = 0;
@@ -229,15 +194,7 @@
                     continue;
                 }
 
-                if (shouldDrawItemDivider(holder, items) && !hasDrawnPredictedAppsDivider) {
-                    // Draw the divider under the predicted apps
-                    int top = child.getTop() + child.getHeight() + mPredictionBarDividerOffset;
-                    c.drawLine(mBackgroundPadding.left, top,
-                            parent.getWidth() - mBackgroundPadding.right, top,
-                            mPredictedAppsDividerPaint);
-                    hasDrawnPredictedAppsDivider = true;
-
-                } else if (showSectionNames && shouldDrawItemSection(holder, i, items)) {
+                if (showSectionNames && shouldDrawItemSection(holder, i, items)) {
                     // At this point, we only draw sections for each section break;
                     int viewTopOffset = (2 * child.getPaddingTop());
                     int pos = holder.getPosition();
@@ -256,7 +213,6 @@
                             continue;
                         }
 
-
                         // Find the section name bounds
                         PointF sectionBounds = getAndCacheSectionBounds(sectionName);
 
@@ -264,7 +220,7 @@
                         int sectionBaseline = (int) (viewTopOffset + sectionBounds.y);
                         int x = mIsRtl ?
                                 parent.getWidth() - mBackgroundPadding.left - mSectionNamesMargin :
-                                        mBackgroundPadding.left;
+                                mBackgroundPadding.left;
                         x += (int) ((mSectionNamesMargin - sectionBounds.x) / 2f);
                         int y = child.getTop() + sectionBaseline;
 
@@ -349,15 +305,6 @@
         }
 
         /**
-         * Returns whether to draw the divider for a given child.
-         */
-        private boolean shouldDrawItemDivider(ViewHolder holder,
-                List<AlphabeticalAppsList.AdapterItem> items) {
-            int pos = holder.getPosition();
-            return items.get(pos).viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE;
-        }
-
-        /**
          * Returns whether to draw the section for the given child.
          */
         private boolean shouldDrawItemSection(ViewHolder holder, int childIndex,
@@ -366,12 +313,12 @@
             AlphabeticalAppsList.AdapterItem item = items.get(pos);
 
             // Ensure it's an icon
-            if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
+            if (item.viewType != AllAppsGridAdapter.VIEW_TYPE_ICON) {
                 return false;
             }
             // Draw the section header for the first item in each section
             return (childIndex == 0) ||
-                    (items.get(pos - 1).viewType == AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE);
+                    (items.get(pos - 1).viewType == AllAppsGridAdapter.VIEW_TYPE_SECTION_BREAK);
         }
     }
 
@@ -381,7 +328,6 @@
     private final GridLayoutManager mGridLayoutMgr;
     private final GridSpanSizer mGridSizer;
     private final GridItemDecoration mItemDecoration;
-    private final View.OnTouchListener mTouchListener;
     private final View.OnClickListener mIconClickListener;
     private final View.OnLongClickListener mIconLongClickListener;
 
@@ -389,30 +335,25 @@
     private final boolean mIsRtl;
 
     // Section drawing
+    @Deprecated
     private final int mSectionNamesMargin;
+    @Deprecated
     private final int mSectionHeaderOffset;
     private final Paint mSectionTextPaint;
-    private final Paint mPredictedAppsDividerPaint;
 
-    private final int mPredictionBarDividerOffset;
     private int mAppsPerRow;
+
     private BindViewCallback mBindViewCallback;
     private AllAppsSearchBarController mSearchController;
+    private OnFocusChangeListener mIconFocusListener;
 
     // The text to show when there are no search results and no market search handler.
     private String mEmptySearchMessage;
-    // The name of the market app which handles searches, to be used in the format str
-    // below when updating the search-market view.  Only needs to be loaded once.
-    private String mMarketAppName;
-    // The text to show when there is a market app which can handle a specific query, updated
-    // each time the search query changes.
-    private String mMarketSearchMessage;
     // The intent to send off to the market app, updated each time the search query changes.
     private Intent mMarketSearchIntent;
 
-    public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps,
-            View.OnTouchListener touchListener, View.OnClickListener iconClickListener,
-            View.OnLongClickListener iconLongClickListener) {
+    public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps, View.OnClickListener
+            iconClickListener, View.OnLongClickListener iconLongClickListener) {
         Resources res = launcher.getResources();
         mLauncher = launcher;
         mApps = apps;
@@ -422,7 +363,6 @@
         mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
         mItemDecoration = new GridItemDecoration();
         mLayoutInflater = LayoutInflater.from(launcher);
-        mTouchListener = touchListener;
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
         mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
@@ -432,14 +372,19 @@
         mSectionTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
         mSectionTextPaint.setTextSize(res.getDimensionPixelSize(
                 R.dimen.all_apps_grid_section_text_size));
-        mSectionTextPaint.setColor(res.getColor(R.color.all_apps_grid_section_text_color));
+        mSectionTextPaint.setColor(Utilities.getColorAccent(launcher));
+    }
 
-        mPredictedAppsDividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
-        mPredictedAppsDividerPaint.setStrokeWidth(Utilities.pxFromDp(1f, res.getDisplayMetrics()));
-        mPredictedAppsDividerPaint.setColor(0x1E000000);
-        mPredictionBarDividerOffset =
-                (-res.getDimensionPixelSize(R.dimen.all_apps_prediction_icon_bottom_padding) +
-                        res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding)) / 2;
+    public static boolean isDividerViewType(int viewType) {
+        return isViewType(viewType, VIEW_TYPE_MASK_DIVIDER);
+    }
+
+    public static boolean isIconViewType(int viewType) {
+        return isViewType(viewType, VIEW_TYPE_MASK_ICON);
+    }
+
+    public static boolean isViewType(int viewType, int viewTypeMask) {
+        return (viewType & viewTypeMask) != 0;
     }
 
     /**
@@ -452,14 +397,10 @@
 
     public void setSearchController(AllAppsSearchBarController searchController) {
         mSearchController = searchController;
+    }
 
-        // Resolve the market app handling additional searches
-        PackageManager pm = mLauncher.getPackageManager();
-        ResolveInfo marketInfo = pm.resolveActivity(mSearchController.createMarketSearchIntent(""),
-                PackageManager.MATCH_DEFAULT_ONLY);
-        if (marketInfo != null) {
-            mMarketAppName = marketInfo.loadLabel(pm).toString();
-        }
+    public void setIconFocusListener(OnFocusChangeListener focusListener) {
+        mIconFocusListener = focusListener;
     }
 
     /**
@@ -468,13 +409,8 @@
      */
     public void setLastSearchQuery(String query) {
         Resources res = mLauncher.getResources();
-        String formatStr = res.getString(R.string.all_apps_no_search_results);
-        mEmptySearchMessage = String.format(formatStr, query);
-        if (mMarketAppName != null) {
-            mMarketSearchMessage = String.format(res.getString(R.string.all_apps_search_market_message),
-                    mMarketAppName);
-            mMarketSearchIntent = mSearchController.createMarketSearchIntent(query);
-        }
+        mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query);
+        mMarketSearchIntent = mSearchController.createMarketSearchIntent(query);
     }
 
     /**
@@ -510,37 +446,32 @@
     @Override
     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         switch (viewType) {
-            case SECTION_BREAK_VIEW_TYPE:
+            case VIEW_TYPE_SECTION_BREAK:
                 return new ViewHolder(new View(parent.getContext()));
-            case ICON_VIEW_TYPE: {
+            case VIEW_TYPE_ICON:
+                /* falls through */
+            case VIEW_TYPE_PREDICTION_ICON: {
                 BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
                         R.layout.all_apps_icon, parent, false);
-                icon.setOnTouchListener(mTouchListener);
                 icon.setOnClickListener(mIconClickListener);
                 icon.setOnLongClickListener(mIconLongClickListener);
                 icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
                         .getLongPressTimeout());
-                icon.setFocusable(true);
+                icon.setOnFocusChangeListener(mIconFocusListener);
+
+                // Ensure the all apps icon height matches the workspace icons
+                DeviceProfile profile = mLauncher.getDeviceProfile();
+                Point cellSize = profile.getCellSize();
+                GridLayoutManager.LayoutParams lp =
+                        (GridLayoutManager.LayoutParams) icon.getLayoutParams();
+                lp.height = cellSize.y;
+                icon.setLayoutParams(lp);
                 return new ViewHolder(icon);
             }
-            case PREDICTION_ICON_VIEW_TYPE: {
-                BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
-                        R.layout.all_apps_prediction_bar_icon, parent, false);
-                icon.setOnTouchListener(mTouchListener);
-                icon.setOnClickListener(mIconClickListener);
-                icon.setOnLongClickListener(mIconLongClickListener);
-                icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
-                        .getLongPressTimeout());
-                icon.setFocusable(true);
-                return new ViewHolder(icon);
-            }
-            case EMPTY_SEARCH_VIEW_TYPE:
+            case VIEW_TYPE_EMPTY_SEARCH:
                 return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
                         parent, false));
-            case SEARCH_MARKET_DIVIDER_VIEW_TYPE:
-                return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_search_market_divider,
-                        parent, false));
-            case SEARCH_MARKET_VIEW_TYPE:
+            case VIEW_TYPE_SEARCH_MARKET:
                 View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
                         parent, false);
                 searchMarketView.setOnClickListener(new View.OnClickListener() {
@@ -550,6 +481,14 @@
                     }
                 });
                 return new ViewHolder(searchMarketView);
+            case VIEW_TYPE_SEARCH_DIVIDER:
+                return new ViewHolder(mLayoutInflater.inflate(
+                        R.layout.all_apps_search_divider, parent, false));
+            case VIEW_TYPE_PREDICTION_DIVIDER:
+                /* falls through */
+            case VIEW_TYPE_SEARCH_MARKET_DIVIDER:
+                return new ViewHolder(mLayoutInflater.inflate(
+                        R.layout.all_apps_divider, parent, false));
             default:
                 throw new RuntimeException("Unexpected view type");
         }
@@ -558,32 +497,30 @@
     @Override
     public void onBindViewHolder(ViewHolder holder, int position) {
         switch (holder.getItemViewType()) {
-            case ICON_VIEW_TYPE: {
+            case VIEW_TYPE_ICON: {
                 AppInfo info = mApps.getAdapterItems().get(position).appInfo;
                 BubbleTextView icon = (BubbleTextView) holder.mContent;
                 icon.applyFromApplicationInfo(info);
+                icon.setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
                 break;
             }
-            case PREDICTION_ICON_VIEW_TYPE: {
+            case VIEW_TYPE_PREDICTION_ICON: {
                 AppInfo info = mApps.getAdapterItems().get(position).appInfo;
                 BubbleTextView icon = (BubbleTextView) holder.mContent;
                 icon.applyFromApplicationInfo(info);
+                icon.setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
                 break;
             }
-            case EMPTY_SEARCH_VIEW_TYPE:
+            case VIEW_TYPE_EMPTY_SEARCH:
                 TextView emptyViewText = (TextView) holder.mContent;
                 emptyViewText.setText(mEmptySearchMessage);
                 emptyViewText.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER :
                         Gravity.START | Gravity.CENTER_VERTICAL);
                 break;
-            case SEARCH_MARKET_VIEW_TYPE:
+            case VIEW_TYPE_SEARCH_MARKET:
                 TextView searchView = (TextView) holder.mContent;
                 if (mMarketSearchIntent != null) {
                     searchView.setVisibility(View.VISIBLE);
-                    searchView.setContentDescription(mMarketSearchMessage);
-                    searchView.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER :
-                            Gravity.START | Gravity.CENTER_VERTICAL);
-                    searchView.setText(mMarketSearchMessage);
                 } else {
                     searchView.setVisibility(View.GONE);
                 }
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 2b3d061..0173847 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -19,35 +19,32 @@
 import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
-import android.os.Bundle;
 import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
+import android.util.SparseIntArray;
 import android.view.View;
 
 import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
-import com.android.launcher3.Stats;
-import com.android.launcher3.Utilities;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 
 import java.util.List;
 
 /**
  * A RecyclerView with custom fast scroll support for the all apps view.
  */
-public class AllAppsRecyclerView extends BaseRecyclerView
-        implements Stats.LaunchSourceProvider {
+public class AllAppsRecyclerView extends BaseRecyclerView {
 
     private AlphabeticalAppsList mApps;
     private AllAppsFastScrollHelper mFastScrollHelper;
-    private BaseRecyclerView.ScrollPositionState mScrollPosState =
-            new BaseRecyclerView.ScrollPositionState();
     private int mNumAppsPerRow;
 
-    // The specific icon heights that we use to calculate scroll
-    private int mPredictionIconHeight;
-    private int mIconHeight;
+    // The specific view heights that we use to calculate scroll
+    private SparseIntArray mViewHeights = new SparseIntArray();
+    private SparseIntArray mCachedScrollPositions = new SparseIntArray();
 
     // The empty-search result background
     private AllAppsBackgroundDrawable mEmptySearchBackground;
@@ -97,20 +94,62 @@
 
         RecyclerView.RecycledViewPool pool = getRecycledViewPool();
         int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
-        pool.setMaxRecycledViews(AllAppsGridAdapter.EMPTY_SEARCH_VIEW_TYPE, 1);
-        pool.setMaxRecycledViews(AllAppsGridAdapter.SEARCH_MARKET_DIVIDER_VIEW_TYPE, 1);
-        pool.setMaxRecycledViews(AllAppsGridAdapter.SEARCH_MARKET_VIEW_TYPE, 1);
-        pool.setMaxRecycledViews(AllAppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow);
-        pool.setMaxRecycledViews(AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE, mNumAppsPerRow);
-        pool.setMaxRecycledViews(AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows);
+        pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH, 1);
+        pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER, 1);
+        pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER, 1);
+        pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET, 1);
+        pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_ICON, approxRows * mNumAppsPerRow);
+        pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON, mNumAppsPerRow);
+        pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER, 1);
+        pool.setMaxRecycledViews(AllAppsGridAdapter.VIEW_TYPE_SECTION_BREAK, approxRows);
     }
 
     /**
-     * Sets the heights of the icons in this view (for scroll calculations).
+     * Ensures that we can present a stable scrollbar for views of varying types by pre-measuring
+     * all the different view types.
      */
-    public void setPremeasuredIconHeights(int predictionIconHeight, int iconHeight) {
-        mPredictionIconHeight = predictionIconHeight;
-        mIconHeight = iconHeight;
+    public void preMeasureViews(AllAppsGridAdapter adapter) {
+        final int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(
+                getResources().getDisplayMetrics().widthPixels, View.MeasureSpec.AT_MOST);
+        final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
+                getResources().getDisplayMetrics().heightPixels, View.MeasureSpec.AT_MOST);
+
+        // Icons
+        BubbleTextView icon = (BubbleTextView) adapter.onCreateViewHolder(this,
+                AllAppsGridAdapter.VIEW_TYPE_ICON).mContent;
+        int iconHeight = icon.getLayoutParams().height;
+        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_ICON, iconHeight);
+        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON, iconHeight);
+
+        // Search divider
+        View searchDivider = adapter.onCreateViewHolder(this,
+                AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER).mContent;
+        searchDivider.measure(widthMeasureSpec, heightMeasureSpec);
+        int searchDividerHeight = searchDivider.getMeasuredHeight();
+        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER, searchDividerHeight);
+
+        // Generic dividers
+        View divider = adapter.onCreateViewHolder(this,
+                AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER).mContent;
+        divider.measure(widthMeasureSpec, heightMeasureSpec);
+        int dividerHeight = divider.getMeasuredHeight();
+        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER, dividerHeight);
+        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER, dividerHeight);
+
+        // Search views
+        View emptySearch = adapter.onCreateViewHolder(this,
+                AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH).mContent;
+        emptySearch.measure(widthMeasureSpec, heightMeasureSpec);
+        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH,
+                emptySearch.getMeasuredHeight());
+        View searchMarket = adapter.onCreateViewHolder(this,
+                AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET).mContent;
+        searchMarket.measure(widthMeasureSpec, heightMeasureSpec);
+        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET,
+                searchMarket.getMeasuredHeight());
+
+        // Section breaks
+        mViewHeights.put(AllAppsGridAdapter.VIEW_TYPE_SECTION_BREAK, 0);
     }
 
     /**
@@ -164,12 +203,9 @@
         updateEmptySearchBackgroundBounds();
     }
 
-    @Override
-    public void fillInLaunchSourceData(View v, Bundle sourceData) {
-        sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_ALL_APPS);
+    public int getContainerType(View v) {
         if (mApps.hasFilter()) {
-            sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER,
-                    Stats.SUB_CONTAINER_ALL_APPS_SEARCH);
+            return LauncherLogProto.SEARCHRESULT;
         } else {
             if (v instanceof BubbleTextView) {
                 BubbleTextView icon = (BubbleTextView) v;
@@ -177,15 +213,12 @@
                 if (position != NO_POSITION) {
                     List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
                     AlphabeticalAppsList.AdapterItem item = items.get(position);
-                    if (item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
-                        sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER,
-                                Stats.SUB_CONTAINER_ALL_APPS_PREDICTION);
-                        return;
+                    if (item.viewType == AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON) {
+                        return LauncherLogProto.PREDICTION;
                     }
                 }
             }
-            sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER,
-                    Stats.SUB_CONTAINER_ALL_APPS_A_Z);
+            return LauncherLogProto.ALLAPPS;
         }
     }
 
@@ -234,8 +267,8 @@
         }
 
         // Update the fast scroll
-        int scrollY = getScrollTop(mScrollPosState);
-        int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows());
+        int scrollY = getCurrentScrollY();
+        int availableScrollHeight = getAvailableScrollHeight();
         mFastScrollHelper.smoothScrollToSection(scrollY, availableScrollHeight, lastInfo);
         return lastInfo.sectionName;
     }
@@ -249,6 +282,11 @@
     @Override
     public void setAdapter(Adapter adapter) {
         super.setAdapter(adapter);
+        adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
+            public void onChanged() {
+                mCachedScrollPositions.clear();
+            }
+        });
         mFastScrollHelper.onSetAdapter((AllAppsGridAdapter) adapter);
     }
 
@@ -265,42 +303,30 @@
             return;
         }
 
-        // Find the index and height of the first visible row (all rows have the same height)
-        int rowCount = mApps.getNumAppRows();
-        getCurScrollState(mScrollPosState, -1);
-        if (mScrollPosState.rowIndex < 0) {
+        // Skip early if, there no child laid out in the container.
+        int scrollY = getCurrentScrollY();
+        if (scrollY < 0) {
             mScrollbar.setThumbOffset(-1, -1);
             return;
         }
 
         // Only show the scrollbar if there is height to be scrolled
         int availableScrollBarHeight = getAvailableScrollBarHeight();
-        int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows());
+        int availableScrollHeight = getAvailableScrollHeight();
         if (availableScrollHeight <= 0) {
             mScrollbar.setThumbOffset(-1, -1);
             return;
         }
 
-        // Calculate the current scroll position, the scrollY of the recycler view accounts for the
-        // view padding, while the scrollBarY is drawn right up to the background padding (ignoring
-        // padding)
-        int scrollY = getScrollTop(mScrollPosState);
-        int scrollBarY = mBackgroundPadding.top +
-                (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
-
         if (mScrollbar.isThumbDetached()) {
-            int scrollBarX;
-            if (Utilities.isRtl(getResources())) {
-                scrollBarX = mBackgroundPadding.left;
-            } else {
-                scrollBarX = getWidth() - mBackgroundPadding.right - mScrollbar.getThumbWidth();
-            }
+            if (!mScrollbar.isDraggingThumb()) {
+                // Calculate the current scroll position, the scrollY of the recycler view accounts
+                // for the view padding, while the scrollBarY is drawn right up to the background
+                // padding (ignoring padding)
+                int scrollBarX = getScrollBarX();
+                int scrollBarY = mBackgroundPadding.top +
+                        (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
 
-            if (mScrollbar.isDraggingThumb()) {
-                // If the thumb is detached, then just update the thumb to the current
-                // touch position
-                mScrollbar.setThumbOffset(scrollBarX, (int) mScrollbar.getLastTouchY());
-            } else {
                 int thumbScrollY = mScrollbar.getThumbOffset().y;
                 int diffScrollY = scrollBarY - thumbScrollY;
                 if (diffScrollY * dy > 0f) {
@@ -330,41 +356,10 @@
                 }
             }
         } else {
-            synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount);
+            synchronizeScrollBarThumbOffsetToViewScroll(scrollY, availableScrollHeight);
         }
     }
 
-    /**
-     * Returns the current scroll state of the apps rows.
-     */
-    protected void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask) {
-        stateOut.rowIndex = -1;
-        stateOut.rowTopOffset = -1;
-        stateOut.itemPos = -1;
-
-        // Return early if there are no items or we haven't been measured
-        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
-        if (items.isEmpty() || mNumAppsPerRow == 0) {
-            return;
-        }
-
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            int position = getChildPosition(child);
-            if (position != NO_POSITION) {
-                AlphabeticalAppsList.AdapterItem item = items.get(position);
-                if ((item.viewType & viewTypeMask) != 0) {
-                    stateOut.rowIndex = item.rowIndex;
-                    stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child);
-                    stateOut.itemPos = position;
-                    return;
-                }
-            }
-        }
-        return;
-    }
-
     @Override
     protected boolean supportsFastScrolling() {
         // Only allow fast scrolling when the user is not searching, since the results are not
@@ -372,13 +367,69 @@
         return !mApps.hasFilter();
     }
 
-    protected int getTop(int rowIndex) {
-        if (getChildCount() == 0 || rowIndex <= 0) {
-            return 0;
+    @Override
+    public int getCurrentScrollY() {
+        // Return early if there are no items or we haven't been measured
+        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+        if (items.isEmpty() || mNumAppsPerRow == 0 || getChildCount() == 0) {
+            return -1;
         }
 
-        // The prediction bar icons have more padding, so account for that in the row offset
-        return mPredictionIconHeight + (rowIndex - 1) * mIconHeight;
+        // Calculate the y and offset for the item
+        View child = getChildAt(0);
+        int position = getChildPosition(child);
+        if (position == NO_POSITION) {
+            return -1;
+        }
+        return getCurrentScrollY(position, getLayoutManager().getDecoratedTop(child));
+    }
+
+    public int getCurrentScrollY(int position, int offset) {
+        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+        AlphabeticalAppsList.AdapterItem posItem = position < items.size() ?
+                items.get(position) : null;
+        int y = mCachedScrollPositions.get(position, -1);
+        if (y < 0) {
+            y = 0;
+            for (int i = 0; i < position; i++) {
+                AlphabeticalAppsList.AdapterItem item = items.get(i);
+                if (AllAppsGridAdapter.isIconViewType(item.viewType)) {
+                    // Break once we reach the desired row
+                    if (posItem != null && posItem.viewType == item.viewType &&
+                            posItem.rowIndex == item.rowIndex) {
+                        break;
+                    }
+                    // Otherwise, only account for the first icon in the row since they are the same
+                    // size within a row
+                    if (item.rowAppIndex == 0) {
+                        y += mViewHeights.get(item.viewType, 0);
+                    }
+                } else {
+                    // Rest of the views span the full width
+                    y += mViewHeights.get(item.viewType, 0);
+                }
+            }
+            mCachedScrollPositions.put(position, y);
+        }
+
+        return getPaddingTop() + y - offset;
+    }
+
+    @Override
+    protected int getVisibleHeight() {
+        return super.getVisibleHeight()
+                - Launcher.getLauncher(getContext()).getDragLayer().getInsets().bottom;
+    }
+
+    /**
+     * Returns the available scroll height:
+     *   AvailableScrollHeight = Total height of the all items - last page height
+     */
+    @Override
+    protected int getAvailableScrollHeight() {
+        int paddedHeight = getCurrentScrollY(mApps.getAdapterItems().size(), 0);
+        int totalHeight = paddedHeight + getPaddingBottom();
+        return totalHeight - getVisibleHeight();
     }
 
     /**
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
index 14e2a18..b5afb2b 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
@@ -17,11 +17,11 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
+
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
 import com.android.launcher3.ClickShadowView;
@@ -49,7 +49,7 @@
     public AllAppsRecyclerViewContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
-        Launcher launcher = (Launcher) context;
+        Launcher launcher = Launcher.getLauncher(context);
         DeviceProfile grid = launcher.getDeviceProfile();
 
         mTouchFeedbackView = new ClickShadowView(context);
@@ -65,7 +65,8 @@
             mTouchFeedbackView.setBitmap(null);
             mTouchFeedbackView.animate().cancel();
         } else if (mTouchFeedbackView.setBitmap(background)) {
-            mTouchFeedbackView.alignWithIconView(icon, (ViewGroup) icon.getParent());
+            View rv = findViewById(R.id.apps_list_view);
+            mTouchFeedbackView.alignWithIconView(icon, (ViewGroup) icon.getParent(), rv);
             mTouchFeedbackView.animateShadow();
         }
     }
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
index 39d6dd5..365ab31 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
@@ -20,6 +20,7 @@
 import android.graphics.Rect;
 import android.net.Uri;
 import android.text.Editable;
+import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.view.KeyEvent;
 import android.view.View;
@@ -45,10 +46,14 @@
     protected AlphabeticalAppsList mApps;
     protected Callbacks mCb;
     protected ExtendedEditText mInput;
+    private String mQuery;
 
     protected DefaultAppSearchAlgorithm mSearchAlgorithm;
     protected InputMethodManager mInputMethodManager;
 
+    public void setVisibility(int visibility) {
+        mInput.setVisibility(visibility);
+    }
     /**
      * Sets the references to the apps model and the search result callback.
      */
@@ -87,16 +92,25 @@
 
     @Override
     public void afterTextChanged(final Editable s) {
-        String query = s.toString();
-        if (query.isEmpty()) {
+        mQuery = s.toString();
+        if (mQuery.isEmpty()) {
             mSearchAlgorithm.cancel(true);
             mCb.clearSearchResult();
         } else {
             mSearchAlgorithm.cancel(false);
-            mSearchAlgorithm.doSearch(query, mCb);
+            mSearchAlgorithm.doSearch(mQuery, mCb);
         }
     }
 
+    protected void refreshSearchResult() {
+        if (TextUtils.isEmpty(mQuery)) {
+            return;
+        }
+        // If play store continues auto updating an app, we want to show partial result.
+        mSearchAlgorithm.cancel(false);
+        mSearchAlgorithm.doSearch(mQuery, mCb);
+    }
+
     @Override
     public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
         // Skip if it's not the right action
@@ -113,10 +127,9 @@
 
     @Override
     public boolean onBackKey() {
-        // Only hide the search field if there is no query, or if there
-        // are no filtered results
+        // Only hide the search field if there is no query
         String query = Utilities.trim(mInput.getEditableText().toString());
-        if (query.isEmpty() || mApps.hasNoFilteredResults()) {
+        if (query.isEmpty()) {
             reset();
             return true;
         }
@@ -130,6 +143,11 @@
         unfocusSearchField();
         mCb.clearSearchResult();
         mInput.setText("");
+        mQuery = null;
+        hideKeyboard();
+    }
+
+    protected void hideKeyboard() {
         mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0);
     }
 
@@ -144,8 +162,7 @@
      * Focuses the search field to handle key events.
      */
     public void focusSearchField() {
-        mInput.requestFocus();
-        mInputMethodManager.showSoftInput(mInput, InputMethodManager.SHOW_IMPLICIT);
+        mInput.showKeyboard();
     }
 
     /**
@@ -175,6 +192,7 @@
         /**
          * Called when the bounds of the search bar has changed.
          */
+        @Deprecated
         void onBoundsChanged(Rect newBounds);
 
         /**
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
new file mode 100644
index 0000000..b129bb0
--- /dev/null
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -0,0 +1,551 @@
+package com.android.launcher3.allapps;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
+import android.graphics.Color;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.graphics.ColorUtils;
+import android.support.v4.view.animation.FastOutSlowInInterpolator;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.TouchController;
+
+/**
+ * Handles AllApps view transition.
+ * 1) Slides all apps view using direct manipulation
+ * 2) When finger is released, animate to either top or bottom accordingly.
+ * <p/>
+ * Algorithm:
+ * If release velocity > THRES1, snap according to the direction of movement.
+ * If release velocity < THRES1, snap according to either top or bottom depending on whether it's
+ * closer to top or closer to the page indicator.
+ */
+public class AllAppsTransitionController implements TouchController, VerticalPullDetector.Listener,
+        View.OnLayoutChangeListener {
+
+    private static final String TAG = "AllAppsTrans";
+    private static final boolean DBG = false;
+
+    private final Interpolator mAccelInterpolator = new AccelerateInterpolator(2f);
+    private final Interpolator mDecelInterpolator = new DecelerateInterpolator(3f);
+    private final Interpolator mFastOutSlowInInterpolator = new FastOutSlowInInterpolator();
+    private final ScrollInterpolator mScrollInterpolator = new ScrollInterpolator();
+
+    private static final float ANIMATION_DURATION = 1200;
+    private static final float PARALLAX_COEFFICIENT = .125f;
+    private static final float FAST_FLING_PX_MS = 10;
+    private static final int SINGLE_FRAME_MS = 16;
+
+    private AllAppsContainerView mAppsView;
+    private int mAllAppsBackgroundColor;
+    private Workspace mWorkspace;
+    private Hotseat mHotseat;
+    private int mHotseatBackgroundColor;
+
+    private AllAppsCaretController mCaretController;
+
+    private float mStatusBarHeight;
+
+    private final Launcher mLauncher;
+    private final VerticalPullDetector mDetector;
+    private final ArgbEvaluator mEvaluator;
+
+    // Animation in this class is controlled by a single variable {@link mProgress}.
+    // Visually, it represents top y coordinate of the all apps container if multiplied with
+    // {@link mShiftRange}.
+
+    // When {@link mProgress} is 0, all apps container is pulled up.
+    // When {@link mProgress} is 1, all apps container is pulled down.
+    private float mShiftStart;      // [0, mShiftRange]
+    private float mShiftRange;      // changes depending on the orientation
+    private float mProgress;        // [0, 1], mShiftRange * mProgress = shiftCurrent
+
+    // Velocity of the container. Unit is in px/ms.
+    private float mContainerVelocity;
+
+    private static final float DEFAULT_SHIFT_RANGE = 10;
+
+    private static final float RECATCH_REJECTION_FRACTION = .0875f;
+
+    private int mBezelSwipeUpHeight;
+    private long mAnimationDuration;
+
+    private AnimatorSet mCurrentAnimation;
+    private boolean mNoIntercept;
+
+    // Used in discovery bounce animation to provide the transition without workspace changing.
+    private boolean mIsTranslateWithoutWorkspace = false;
+    private AnimatorSet mDiscoBounceAnimation;
+
+    public AllAppsTransitionController(Launcher l) {
+        mLauncher = l;
+        mDetector = new VerticalPullDetector(l);
+        mDetector.setListener(this);
+        mShiftRange = DEFAULT_SHIFT_RANGE;
+        mProgress = 1f;
+        mBezelSwipeUpHeight = l.getResources().getDimensionPixelSize(
+                R.dimen.all_apps_bezel_swipe_height);
+
+        mEvaluator = new ArgbEvaluator();
+        mAllAppsBackgroundColor = ContextCompat.getColor(l, R.color.all_apps_container_color);
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mNoIntercept = false;
+            if (!mLauncher.isAllAppsVisible() && mLauncher.getWorkspace().workspaceInModalState()) {
+                mNoIntercept = true;
+            } else if (mLauncher.isAllAppsVisible() &&
+                    !mAppsView.shouldContainerScroll(ev)) {
+                mNoIntercept = true;
+            } else if (!mLauncher.isAllAppsVisible() && !shouldPossiblyIntercept(ev)) {
+                mNoIntercept = true;
+            } else {
+                // Now figure out which direction scroll events the controller will start
+                // calling the callbacks.
+                int directionsToDetectScroll = 0;
+                boolean ignoreSlopWhenSettling = false;
+
+                if (mDetector.isIdleState()) {
+                    if (mLauncher.isAllAppsVisible()) {
+                        directionsToDetectScroll |= VerticalPullDetector.DIRECTION_DOWN;
+                    } else {
+                        directionsToDetectScroll |= VerticalPullDetector.DIRECTION_UP;
+                    }
+                } else {
+                    if (isInDisallowRecatchBottomZone()) {
+                        directionsToDetectScroll |= VerticalPullDetector.DIRECTION_UP;
+                    } else if (isInDisallowRecatchTopZone()) {
+                        directionsToDetectScroll |= VerticalPullDetector.DIRECTION_DOWN;
+                    } else {
+                        directionsToDetectScroll |= VerticalPullDetector.DIRECTION_BOTH;
+                        ignoreSlopWhenSettling = true;
+                    }
+                }
+                mDetector.setDetectableScrollConditions(directionsToDetectScroll,
+                        ignoreSlopWhenSettling);
+            }
+        }
+        if (mNoIntercept) {
+            return false;
+        }
+        mDetector.onTouchEvent(ev);
+        if (mDetector.isSettlingState() && (isInDisallowRecatchBottomZone() || isInDisallowRecatchTopZone())) {
+            return false;
+        }
+        return mDetector.isDraggingOrSettling();
+    }
+
+    private boolean shouldPossiblyIntercept(MotionEvent ev) {
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        if (mDetector.isIdleState()) {
+            if (grid.isVerticalBarLayout()) {
+                if (ev.getY() > mLauncher.getDeviceProfile().heightPx - mBezelSwipeUpHeight) {
+                    return true;
+                }
+            } else {
+                if (mLauncher.getDragLayer().isEventOverHotseat(ev) ||
+                        mLauncher.getDragLayer().isEventOverPageIndicator(ev)) {
+                    return true;
+                }
+            }
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        return mDetector.onTouchEvent(ev);
+    }
+
+    private boolean isInDisallowRecatchTopZone() {
+        return mProgress < RECATCH_REJECTION_FRACTION;
+    }
+
+    private boolean isInDisallowRecatchBottomZone() {
+        return mProgress > 1 - RECATCH_REJECTION_FRACTION;
+    }
+
+    @Override
+    public void onDragStart(boolean start) {
+        mCaretController.onDragStart();
+        cancelAnimation();
+        mCurrentAnimation = LauncherAnimUtils.createAnimatorSet();
+        mShiftStart = mAppsView.getTranslationY();
+        preparePull(start);
+    }
+
+    @Override
+    public boolean onDrag(float displacement, float velocity) {
+        if (mAppsView == null) {
+            return false;   // early termination.
+        }
+
+        mContainerVelocity = velocity;
+
+        float shift = Math.min(Math.max(0, mShiftStart + displacement), mShiftRange);
+        setProgress(shift / mShiftRange);
+
+        return true;
+    }
+
+    @Override
+    public void onDragEnd(float velocity, boolean fling) {
+        if (mAppsView == null) {
+            return; // early termination.
+        }
+
+        if (fling) {
+            if (velocity < 0) {
+                calculateDuration(velocity, mAppsView.getTranslationY());
+
+                if (!mLauncher.isAllAppsVisible()) {
+                    mLauncher.getUserEventDispatcher().logActionOnContainer(
+                            LauncherLogProto.Action.FLING,
+                            LauncherLogProto.Action.UP,
+                            LauncherLogProto.HOTSEAT);
+                }
+                mLauncher.showAppsView(true /* animated */,
+                        false /* updatePredictedApps */,
+                        false /* focusSearchBar */);
+            } else {
+                calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY()));
+                mLauncher.showWorkspace(true);
+            }
+            // snap to top or bottom using the release velocity
+        } else {
+            if (mAppsView.getTranslationY() > mShiftRange / 2) {
+                calculateDuration(velocity, Math.abs(mShiftRange - mAppsView.getTranslationY()));
+                mLauncher.showWorkspace(true);
+            } else {
+                calculateDuration(velocity, Math.abs(mAppsView.getTranslationY()));
+                if (!mLauncher.isAllAppsVisible()) {
+                    mLauncher.getUserEventDispatcher().logActionOnContainer(
+                            LauncherLogProto.Action.SWIPE,
+                            LauncherLogProto.Action.UP,
+                            LauncherLogProto.HOTSEAT);
+                }
+                mLauncher.showAppsView(true, /* animated */
+                        false /* updatePredictedApps */,
+                        false /* focusSearchBar */);
+            }
+        }
+    }
+
+    public boolean isTransitioning() {
+        return mDetector.isDraggingOrSettling();
+    }
+
+    /**
+     * @param start {@code true} if start of new drag.
+     */
+    public void preparePull(boolean start) {
+        if (start) {
+            // Initialize values that should not change until #onDragEnd
+            mStatusBarHeight = mLauncher.getDragLayer().getInsets().top;
+            mHotseat.setVisibility(View.VISIBLE);
+            mHotseatBackgroundColor = mHotseat.getBackgroundDrawableColor();
+            mHotseat.setBackgroundTransparent(true /* transparent */);
+            if (!mLauncher.isAllAppsVisible()) {
+                mLauncher.tryAndUpdatePredictedApps();
+                mAppsView.setVisibility(View.VISIBLE);
+                mAppsView.setRevealDrawableColor(mHotseatBackgroundColor);
+            }
+        }
+    }
+
+    private void updateLightStatusBar(float shift) {
+        // Do not modify status bar on landscape as all apps is not full bleed.
+        if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            return;
+        }
+        // Use a light status bar (dark icons) if all apps is behind at least half of the status
+        // bar. If the status bar is already light due to wallpaper extraction, keep it that way.
+        boolean forceLight = shift <= mStatusBarHeight / 2;
+        mLauncher.activateLightStatusBar(forceLight);
+    }
+
+    /**
+     * @param progress       value between 0 and 1, 0 shows all apps and 1 shows workspace
+     */
+    public void setProgress(float progress) {
+        float shiftPrevious = mProgress * mShiftRange;
+        mProgress = progress;
+        float shiftCurrent = progress * mShiftRange;
+
+        float workspaceHotseatAlpha = Utilities.boundToRange(progress, 0f, 1f);
+        float alpha = 1 - workspaceHotseatAlpha;
+        float interpolation = mAccelInterpolator.getInterpolation(workspaceHotseatAlpha);
+
+        int color = (Integer) mEvaluator.evaluate(mDecelInterpolator.getInterpolation(alpha),
+                mHotseatBackgroundColor, mAllAppsBackgroundColor);
+        int bgAlpha = Color.alpha((int) mEvaluator.evaluate(alpha,
+                mHotseatBackgroundColor, mAllAppsBackgroundColor));
+
+        mAppsView.setRevealDrawableColor(ColorUtils.setAlphaComponent(color, bgAlpha));
+        mAppsView.getContentView().setAlpha(alpha);
+        mAppsView.setTranslationY(shiftCurrent);
+
+        if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            mWorkspace.setHotseatTranslationAndAlpha(Workspace.Direction.Y, -mShiftRange + shiftCurrent,
+                    interpolation);
+        } else {
+            mWorkspace.setHotseatTranslationAndAlpha(Workspace.Direction.Y,
+                    PARALLAX_COEFFICIENT * (-mShiftRange + shiftCurrent),
+                    interpolation);
+        }
+
+        if (mIsTranslateWithoutWorkspace) {
+            return;
+        }
+        mWorkspace.setWorkspaceYTranslationAndAlpha(
+                PARALLAX_COEFFICIENT * (-mShiftRange + shiftCurrent), interpolation);
+
+        if (!mDetector.isDraggingState()) {
+            mContainerVelocity = mDetector.computeVelocity(shiftCurrent - shiftPrevious,
+                    System.currentTimeMillis());
+        }
+
+        mCaretController.updateCaret(progress, mContainerVelocity, mDetector.isDraggingState());
+        updateLightStatusBar(shiftCurrent);
+    }
+
+    public float getProgress() {
+        return mProgress;
+    }
+
+    private void calculateDuration(float velocity, float disp) {
+        // TODO: make these values constants after tuning.
+        float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity));
+        float travelDistance = Math.max(0.2f, disp / mShiftRange);
+        mAnimationDuration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
+        if (DBG) {
+            Log.d(TAG, String.format("calculateDuration=%d, v=%f, d=%f", mAnimationDuration, velocity, disp));
+        }
+    }
+
+    public boolean animateToAllApps(AnimatorSet animationOut, long duration) {
+        boolean shouldPost = true;
+        if (animationOut == null) {
+            return shouldPost;
+        }
+        Interpolator interpolator;
+        if (mDetector.isIdleState()) {
+            preparePull(true);
+            mAnimationDuration = duration;
+            mShiftStart = mAppsView.getTranslationY();
+            interpolator = mFastOutSlowInInterpolator;
+        } else {
+            mScrollInterpolator.setVelocityAtZero(Math.abs(mContainerVelocity));
+            interpolator = mScrollInterpolator;
+            float nextFrameProgress = mProgress + mContainerVelocity * SINGLE_FRAME_MS / mShiftRange;
+            if (nextFrameProgress >= 0f) {
+                mProgress = nextFrameProgress;
+            }
+            shouldPost = false;
+        }
+
+        ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress",
+                mProgress, 0f);
+        driftAndAlpha.setDuration(mAnimationDuration);
+        driftAndAlpha.setInterpolator(interpolator);
+        animationOut.play(driftAndAlpha);
+
+        animationOut.addListener(new AnimatorListenerAdapter() {
+            boolean canceled = false;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                canceled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (canceled) {
+                    return;
+                } else {
+                    finishPullUp();
+                    cleanUpAnimation();
+                    mDetector.finishedScrolling();
+                }
+            }
+        });
+        mCurrentAnimation = animationOut;
+        return shouldPost;
+    }
+
+    public void showDiscoveryBounce() {
+        // cancel existing animation in case user locked and unlocked at a super human speed.
+        cancelDiscoveryAnimation();
+
+        // assumption is that this variable is always null
+        mDiscoBounceAnimation = (AnimatorSet) AnimatorInflater.loadAnimator(mLauncher,
+                R.anim.discovery_bounce);
+        mDiscoBounceAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animator) {
+                mIsTranslateWithoutWorkspace = true;
+                preparePull(true);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animator) {
+                finishPullDown();
+                mDiscoBounceAnimation = null;
+                mIsTranslateWithoutWorkspace = false;
+            }
+        });
+        mDiscoBounceAnimation.setTarget(this);
+        mAppsView.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mDiscoBounceAnimation == null) {
+                    return;
+                }
+                mDiscoBounceAnimation.start();
+            }
+        });
+    }
+
+    public boolean animateToWorkspace(AnimatorSet animationOut, long duration) {
+        boolean shouldPost = true;
+        if (animationOut == null) {
+            return shouldPost;
+        }
+        Interpolator interpolator;
+        if (mDetector.isIdleState()) {
+            preparePull(true);
+            mAnimationDuration = duration;
+            mShiftStart = mAppsView.getTranslationY();
+            interpolator = mFastOutSlowInInterpolator;
+        } else {
+            mScrollInterpolator.setVelocityAtZero(Math.abs(mContainerVelocity));
+            interpolator = mScrollInterpolator;
+            float nextFrameProgress = mProgress + mContainerVelocity * SINGLE_FRAME_MS / mShiftRange;
+            if (nextFrameProgress <= 1f) {
+                mProgress = nextFrameProgress;
+            }
+            shouldPost = false;
+        }
+
+        ObjectAnimator driftAndAlpha = ObjectAnimator.ofFloat(this, "progress",
+                mProgress, 1f);
+        driftAndAlpha.setDuration(mAnimationDuration);
+        driftAndAlpha.setInterpolator(interpolator);
+        animationOut.play(driftAndAlpha);
+
+        animationOut.addListener(new AnimatorListenerAdapter() {
+            boolean canceled = false;
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                canceled = true;
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (canceled) {
+                    return;
+                } else {
+                    finishPullDown();
+                    cleanUpAnimation();
+                    mDetector.finishedScrolling();
+                }
+            }
+        });
+        mCurrentAnimation = animationOut;
+        return shouldPost;
+    }
+
+    public void finishPullUp() {
+        mHotseat.setVisibility(View.INVISIBLE);
+        setProgress(0f);
+    }
+
+    public void finishPullDown() {
+        mAppsView.setVisibility(View.INVISIBLE);
+        mHotseat.setBackgroundTransparent(false /* transparent */);
+        mHotseat.setVisibility(View.VISIBLE);
+        mAppsView.reset();
+        setProgress(1f);
+    }
+
+    private void cancelAnimation() {
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.cancel();
+            mCurrentAnimation = null;
+        }
+        cancelDiscoveryAnimation();
+    }
+
+    public void cancelDiscoveryAnimation() {
+        if (mDiscoBounceAnimation == null) {
+            return;
+        }
+        mDiscoBounceAnimation.cancel();
+        mDiscoBounceAnimation = null;
+    }
+
+    private void cleanUpAnimation() {
+        mCurrentAnimation = null;
+    }
+
+    public void setupViews(AllAppsContainerView appsView, Hotseat hotseat, Workspace workspace) {
+        mAppsView = appsView;
+        mHotseat = hotseat;
+        mWorkspace = workspace;
+        mHotseat.addOnLayoutChangeListener(this);
+        mHotseat.bringToFront();
+        mCaretController = new AllAppsCaretController(
+                mWorkspace.getPageIndicator().getCaretDrawable(), mLauncher);
+    }
+
+    @Override
+    public void onLayoutChange(View v, int left, int top, int right, int bottom,
+            int oldLeft, int oldTop, int oldRight, int oldBottom) {
+        if (!mLauncher.getDeviceProfile().isVerticalBarLayout()) {
+            mShiftRange = top;
+        } else {
+            mShiftRange = bottom;
+        }
+        setProgress(mProgress);
+    }
+
+    static class ScrollInterpolator implements Interpolator {
+
+        boolean mSteeper;
+
+        public void setVelocityAtZero(float velocity) {
+            mSteeper = velocity > FAST_FLING_PX_MS;
+        }
+
+        public float getInterpolation(float t) {
+            t -= 1.0f;
+            float output = t * t * t;
+            if (mSteeper) {
+                output *= t * t; // Make interpolation initial slope steeper
+            }
+            return output + 1;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index b533ce9..173065b 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -16,13 +16,13 @@
 package com.android.launcher3.allapps;
 
 import android.content.Context;
-import android.support.v7.widget.RecyclerView;
 import android.util.Log;
+
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.config.ProviderConfig;
 import com.android.launcher3.model.AppNameComparator;
 import com.android.launcher3.util.ComponentKey;
 
@@ -108,7 +108,7 @@
 
         public static AdapterItem asSectionBreak(int pos, SectionInfo section) {
             AdapterItem item = new AdapterItem();
-            item.viewType = AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE;
+            item.viewType = AllAppsGridAdapter.VIEW_TYPE_SECTION_BREAK;
             item.position = pos;
             item.sectionInfo = section;
             section.sectionBreakItem = item;
@@ -118,14 +118,14 @@
         public static AdapterItem asPredictedApp(int pos, SectionInfo section, String sectionName,
                 int sectionAppIndex, AppInfo appInfo, int appIndex) {
             AdapterItem item = asApp(pos, section, sectionName, sectionAppIndex, appInfo, appIndex);
-            item.viewType = AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE;
+            item.viewType = AllAppsGridAdapter.VIEW_TYPE_PREDICTION_ICON;
             return item;
         }
 
         public static AdapterItem asApp(int pos, SectionInfo section, String sectionName,
                 int sectionAppIndex, AppInfo appInfo, int appIndex) {
             AdapterItem item = new AdapterItem();
-            item.viewType = AllAppsGridAdapter.ICON_VIEW_TYPE;
+            item.viewType = AllAppsGridAdapter.VIEW_TYPE_ICON;
             item.position = pos;
             item.sectionInfo = section;
             item.sectionName = sectionName;
@@ -137,21 +137,35 @@
 
         public static AdapterItem asEmptySearch(int pos) {
             AdapterItem item = new AdapterItem();
-            item.viewType = AllAppsGridAdapter.EMPTY_SEARCH_VIEW_TYPE;
+            item.viewType = AllAppsGridAdapter.VIEW_TYPE_EMPTY_SEARCH;
             item.position = pos;
             return item;
         }
 
-        public static AdapterItem asDivider(int pos) {
+        public static AdapterItem asPredictionDivider(int pos) {
             AdapterItem item = new AdapterItem();
-            item.viewType = AllAppsGridAdapter.SEARCH_MARKET_DIVIDER_VIEW_TYPE;
+            item.viewType = AllAppsGridAdapter.VIEW_TYPE_PREDICTION_DIVIDER;
+            item.position = pos;
+            return item;
+        }
+
+        public static AdapterItem asSearchDivder(int pos) {
+            AdapterItem item = new AdapterItem();
+            item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_DIVIDER;
+            item.position = pos;
+            return item;
+        }
+
+        public static AdapterItem asMarketDivider(int pos) {
+            AdapterItem item = new AdapterItem();
+            item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET_DIVIDER;
             item.position = pos;
             return item;
         }
 
         public static AdapterItem asMarketSearch(int pos) {
             AdapterItem item = new AdapterItem();
-            item.viewType = AllAppsGridAdapter.SEARCH_MARKET_VIEW_TYPE;
+            item.viewType = AllAppsGridAdapter.VIEW_TYPE_SEARCH_MARKET;
             item.position = pos;
             return item;
         }
@@ -186,7 +200,7 @@
     // The of ordered component names as a result of a search query
     private ArrayList<ComponentKey> mSearchResults;
     private HashMap<CharSequence, String> mCachedSectionNames = new HashMap<>();
-    private RecyclerView.Adapter mAdapter;
+    private AllAppsGridAdapter mAdapter;
     private AlphabeticIndexCompat mIndexer;
     private AppNameComparator mAppNameComparator;
     private MergeAlgorithm mMergeAlgorithm;
@@ -195,7 +209,7 @@
     private int mNumAppRowsInAdapter;
 
     public AlphabeticalAppsList(Context context) {
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
         mIndexer = new AlphabeticIndexCompat(context);
         mAppNameComparator = new AppNameComparator(context);
     }
@@ -215,7 +229,7 @@
     /**
      * Sets the adapter to notify when this dataset changes.
      */
-    public void setAdapter(RecyclerView.Adapter adapter) {
+    public void setAdapter(AllAppsGridAdapter adapter) {
         mAdapter = adapter;
     }
 
@@ -275,10 +289,6 @@
         return (mSearchResults != null) && mFilteredApps.isEmpty();
     }
 
-    public boolean hasPredictedComponents() {
-        return (mPredictedAppComponents != null && mPredictedAppComponents.size() > 0);
-    }
-
     /**
      * Sets the sorted list of filtered components.
      */
@@ -418,6 +428,9 @@
             }
         }
 
+        // Add the search divider
+        mAdapterItems.add(AdapterItem.asSearchDivder(position++));
+
         // Process the predicted app components
         mPredictedApps.clear();
         if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) {
@@ -426,8 +439,8 @@
                 if (info != null) {
                     mPredictedApps.add(info);
                 } else {
-                    if (LauncherAppState.isDogfoodBuild()) {
-                        Log.e(TAG, "Predicted app not found: " + ck.flattenToString(mLauncher));
+                    if (ProviderConfig.IS_DOGFOOD_BUILD) {
+                        Log.e(TAG, "Predicted app not found: " + ck);
                     }
                 }
                 // Stop at the number of predicted apps
@@ -456,6 +469,8 @@
                     mAdapterItems.add(appItem);
                     mFilteredApps.add(info);
                 }
+
+                mAdapterItems.add(AdapterItem.asPredictionDivider(position++));
             }
         }
 
@@ -495,7 +510,7 @@
             if (hasNoFilteredResults()) {
                 mAdapterItems.add(AdapterItem.asEmptySearch(position++));
             } else {
-                mAdapterItems.add(AdapterItem.asDivider(position++));
+                mAdapterItems.add(AdapterItem.asMarketDivider(position++));
             }
             mAdapterItems.add(AdapterItem.asMarketSearch(position++));
         }
@@ -511,10 +526,9 @@
             int rowIndex = -1;
             for (AdapterItem item : mAdapterItems) {
                 item.rowIndex = 0;
-                if (item.viewType == AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE) {
+                if (AllAppsGridAdapter.isDividerViewType(item.viewType)) {
                     numAppsInSection = 0;
-                } else if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE ||
-                        item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
+                } else if (AllAppsGridAdapter.isIconViewType(item.viewType)) {
                     if (numAppsInSection % mNumAppsPerRow == 0) {
                         numAppsInRow = 0;
                         rowIndex++;
@@ -533,8 +547,7 @@
                     float rowFraction = 1f / mNumAppRowsInAdapter;
                     for (FastScrollSectionInfo info : mFastScrollerSections) {
                         AdapterItem item = info.fastScrollToItem;
-                        if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE &&
-                                item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
+                        if (!AllAppsGridAdapter.isIconViewType(item.viewType)) {
                             info.touchFraction = 0f;
                             continue;
                         }
@@ -548,8 +561,7 @@
                     float cumulativeTouchFraction = 0f;
                     for (FastScrollSectionInfo info : mFastScrollerSections) {
                         AdapterItem item = info.fastScrollToItem;
-                        if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE &&
-                                item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
+                        if (!AllAppsGridAdapter.isIconViewType(item.viewType)) {
                             info.touchFraction = 0f;
                             continue;
                         }
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java b/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
index 10740ec..06cf9aa 100644
--- a/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
+++ b/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithm.java
@@ -22,15 +22,12 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.regex.Pattern;
 
 /**
  * The default search implementation.
  */
 public class DefaultAppSearchAlgorithm {
 
-    private static final Pattern SPLIT_PATTERN = Pattern.compile("[\\s|\\p{javaSpaceChar}]+");
-
     private final List<AppInfo> mApps;
     protected final Handler mResultHandler;
 
@@ -61,34 +58,79 @@
         // Do an intersection of the words in the query and each title, and filter out all the
         // apps that don't match all of the words in the query.
         final String queryTextLower = query.toLowerCase();
-        final String[] queryWords = SPLIT_PATTERN.split(queryTextLower);
-
         final ArrayList<ComponentKey> result = new ArrayList<>();
         for (AppInfo info : mApps) {
-            if (matches(info, queryWords)) {
+            if (matches(info, queryTextLower)) {
                 result.add(info.toComponentKey());
             }
         }
         return result;
     }
 
-    protected boolean matches(AppInfo info, String[] queryWords) {
+    protected boolean matches(AppInfo info, String query) {
+        int queryLength = query.length();
+
         String title = info.title.toString();
-        String[] words = SPLIT_PATTERN.split(title.toLowerCase());
-        for (int qi = 0; qi < queryWords.length; qi++) {
-            boolean foundMatch = false;
-            for (int i = 0; i < words.length; i++) {
-                if (words[i].startsWith(queryWords[qi])) {
-                    foundMatch = true;
-                    break;
-                }
-            }
-            if (!foundMatch) {
-                // If there is a word in the query that does not match any words in this
-                // title, so skip it.
-                return false;
+        int titleLength = title.length();
+
+        if (titleLength < queryLength || queryLength <= 0) {
+            return false;
+        }
+
+        int lastType;
+        int thisType = Character.UNASSIGNED;
+        int nextType = Character.getType(title.codePointAt(0));
+
+        int end = titleLength - queryLength;
+        for (int i = 0; i <= end; i++) {
+            lastType = thisType;
+            thisType = nextType;
+            nextType = i < (titleLength - 1) ?
+                    Character.getType(title.codePointAt(i + 1)) : Character.UNASSIGNED;
+            if (isBreak(thisType, lastType, nextType) &&
+                    title.substring(i, i + queryLength).equalsIgnoreCase(query)) {
+                return true;
             }
         }
-        return true;
+        return false;
+    }
+
+    /**
+     * Returns true if the current point should be a break point. Following cases
+     * are considered as break points:
+     *      1) Any non space character after a space character
+     *      2) Any digit after a non-digit character
+     *      3) Any capital character after a digit or small character
+     *      4) Any capital character before a small character
+     */
+    protected boolean isBreak(int thisType, int prevType, int nextType) {
+        switch (thisType) {
+            case Character.UPPERCASE_LETTER:
+                if (nextType == Character.UPPERCASE_LETTER) {
+                    return true;
+                }
+                // Follow through
+            case Character.TITLECASE_LETTER:
+                // Break point if previous was not a upper case
+                return prevType != Character.UPPERCASE_LETTER;
+            case Character.LOWERCASE_LETTER:
+                // Break point if previous was not a letter.
+                return prevType > Character.OTHER_LETTER || prevType <= Character.UNASSIGNED;
+            case Character.DECIMAL_DIGIT_NUMBER:
+            case Character.LETTER_NUMBER:
+            case Character.OTHER_NUMBER:
+                // Break point if previous was not a number
+                return !(prevType == Character.DECIMAL_DIGIT_NUMBER
+                        || prevType == Character.LETTER_NUMBER
+                        || prevType == Character.OTHER_NUMBER);
+            case Character.MATH_SYMBOL:
+            case Character.CURRENCY_SYMBOL:
+            case Character.OTHER_PUNCTUATION:
+            case Character.DASH_PUNCTUATION:
+                // Always a break point for a symbol
+                return true;
+            default:
+                return false;
+        }
     }
 }
diff --git a/src/com/android/launcher3/allapps/HeaderElevationController.java b/src/com/android/launcher3/allapps/HeaderElevationController.java
index 07f583c..ce9837c 100644
--- a/src/com/android/launcher3/allapps/HeaderElevationController.java
+++ b/src/com/android/launcher3/allapps/HeaderElevationController.java
@@ -2,6 +2,7 @@
 
 import android.annotation.TargetApi;
 import android.content.res.Resources;
+import android.graphics.Outline;
 import android.graphics.Rect;
 import android.graphics.drawable.GradientDrawable;
 import android.os.Build;
@@ -11,6 +12,7 @@
 import android.view.ViewOutlineProvider;
 import android.widget.FrameLayout;
 
+import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.R;
 
 /**
@@ -27,7 +29,7 @@
 
     @Override
     public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
-        mCurrentY += dy;
+        mCurrentY = ((BaseRecyclerView) recyclerView).getCurrentScrollY();
         onScroll(mCurrentY);
     }
 
@@ -82,11 +84,31 @@
 
         public ControllerVL(View header) {
             mHeader = header;
-            mHeader.setOutlineProvider(ViewOutlineProvider.BOUNDS);
-
-            Resources res = header.getContext().getResources();
+            Resources res = mHeader.getContext().getResources();
             mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation);
             mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
+
+            // We need to provide a custom outline so the shadow only appears on the bottom edge.
+            // The top, left and right edges are all extended out, and the shadow is clipped
+            // by the parent.
+            final ViewOutlineProvider vop = new ViewOutlineProvider() {
+                @Override
+                public void getOutline(View view, Outline outline) {
+                    final View parent = (View) mHeader.getParent();
+
+                    final int left = parent.getLeft(); // Use the parent to account for offsets
+                    final int top = view.getTop();
+                    final int right = left + view.getWidth();
+                    final int bottom = view.getBottom();
+
+                    outline.setRect(
+                            left - (int) mMaxElevation,
+                            top - (int) mMaxElevation,
+                            right + (int) mMaxElevation,
+                            bottom);
+                }
+            };
+            mHeader.setOutlineProvider(vop);
         }
 
         @Override
diff --git a/src/com/android/launcher3/allapps/VerticalPullDetector.java b/src/com/android/launcher3/allapps/VerticalPullDetector.java
new file mode 100644
index 0000000..ab2b6ed
--- /dev/null
+++ b/src/com/android/launcher3/allapps/VerticalPullDetector.java
@@ -0,0 +1,275 @@
+package com.android.launcher3.allapps;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+/**
+ * One dimensional scroll gesture detector for all apps container pull up interaction.
+ * Client (e.g., AllAppsTransitionController) of this class can register a listener.
+ * <p/>
+ * Features that this gesture detector can support.
+ */
+public class VerticalPullDetector {
+
+    private static final boolean DBG = false;
+    private static final String TAG = "VerticalPullDetector";
+
+    private float mTouchSlop;
+
+    private int mScrollConditions;
+    public static final int DIRECTION_UP = 1 << 0;
+    public static final int DIRECTION_DOWN = 1 << 1;
+    public static final int DIRECTION_BOTH = DIRECTION_DOWN | DIRECTION_UP;
+
+    /**
+     * The minimum release velocity in pixels per millisecond that triggers fling..
+     */
+    public static final float RELEASE_VELOCITY_PX_MS = 1.0f;
+
+    /**
+     * The time constant used to calculate dampening in the low-pass filter of scroll velocity.
+     * Cutoff frequency is set at 10 Hz.
+     */
+    public static final float SCROLL_VELOCITY_DAMPENING_RC = 1000f / (2f * (float) Math.PI * 10);
+
+    /* Scroll state, this is set to true during dragging and animation. */
+    private ScrollState mState = ScrollState.IDLE;
+
+    enum ScrollState {
+        IDLE,
+        DRAGGING,      // onDragStart, onDrag
+        SETTLING       // onDragEnd
+    }
+
+    ;
+
+    //------------------- ScrollState transition diagram -----------------------------------
+    //
+    // IDLE ->      (mDisplacement > mTouchSlop) -> DRAGGING
+    // DRAGGING -> (MotionEvent#ACTION_UP, MotionEvent#ACTION_CANCEL) -> SETTLING
+    // SETTLING -> (MotionEvent#ACTION_DOWN) -> DRAGGING
+    // SETTLING -> (View settled) -> IDLE
+
+    private void setState(ScrollState newState) {
+        if (DBG) {
+            Log.d(TAG, "setState:" + mState + "->" + newState);
+        }
+        // onDragStart and onDragEnd is reported ONLY on state transition
+        if (newState == ScrollState.DRAGGING) {
+            initializeDragging();
+            if (mState == ScrollState.IDLE) {
+                reportDragStart(false /* recatch */);
+            } else if (mState == ScrollState.SETTLING) {
+                reportDragStart(true /* recatch */);
+            }
+        }
+        if (newState == ScrollState.SETTLING) {
+            reportDragEnd();
+        }
+
+        mState = newState;
+    }
+
+    public boolean isDraggingOrSettling() {
+        return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
+    }
+
+    /**
+     * There's no touch and there's no animation.
+     */
+    public boolean isIdleState() {
+        return mState == ScrollState.IDLE;
+    }
+
+    public boolean isSettlingState() {
+        return mState == ScrollState.SETTLING;
+    }
+
+    public boolean isDraggingState() {
+        return mState == ScrollState.DRAGGING;
+    }
+
+    private float mDownX;
+    private float mDownY;
+
+    private float mLastY;
+    private long mCurrentMillis;
+
+    private float mVelocity;
+    private float mLastDisplacement;
+    private float mDisplacementY;
+    private float mDisplacementX;
+
+    private float mSubtractDisplacement;
+    private boolean mIgnoreSlopWhenSettling;
+
+    /* Client of this gesture detector can register a callback. */
+    Listener mListener;
+
+    public void setListener(Listener l) {
+        mListener = l;
+    }
+
+    interface Listener {
+        void onDragStart(boolean start);
+
+        boolean onDrag(float displacement, float velocity);
+
+        void onDragEnd(float velocity, boolean fling);
+    }
+
+    public VerticalPullDetector(Context context) {
+        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+    }
+
+    public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
+        mScrollConditions = scrollDirectionFlags;
+        mIgnoreSlopWhenSettling = ignoreSlop;
+    }
+
+    private boolean shouldScrollStart() {
+        // reject cases where the slop condition is not met.
+        if (Math.abs(mDisplacementY) < mTouchSlop) {
+            return false;
+        }
+
+        // reject cases where the angle condition is not met.
+        float deltaY = Math.abs(mDisplacementY);
+        float deltaX = Math.max(Math.abs(mDisplacementX), 1);
+        if (deltaX > deltaY) {
+            return false;
+        }
+        // Check if the client is interested in scroll in current direction.
+        if (((mScrollConditions & DIRECTION_DOWN) > 0 && mDisplacementY > 0) ||
+                ((mScrollConditions & DIRECTION_UP) > 0 && mDisplacementY < 0)) {
+            return true;
+        }
+        return false;
+    }
+
+    public boolean onTouchEvent(MotionEvent ev) {
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mDownX = ev.getX();
+                mDownY = ev.getY();
+                mLastDisplacement = 0;
+                mDisplacementY = 0;
+                mVelocity = 0;
+
+                if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
+                    setState(ScrollState.DRAGGING);
+                }
+                break;
+            case MotionEvent.ACTION_MOVE:
+                mDisplacementX = ev.getX() - mDownX;
+                mDisplacementY = ev.getY() - mDownY;
+                computeVelocity(ev);
+
+                // handle state and listener calls.
+                if (mState != ScrollState.DRAGGING && shouldScrollStart()) {
+                    setState(ScrollState.DRAGGING);
+                }
+                if (mState == ScrollState.DRAGGING) {
+                    reportDragging();
+                }
+                break;
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                // These are synthetic events and there is no need to update internal values.
+                if (mState == ScrollState.DRAGGING) {
+                    setState(ScrollState.SETTLING);
+                }
+                break;
+            default:
+                //TODO: add multi finger tracking by tracking active pointer.
+                break;
+        }
+        // Do house keeping.
+        mLastDisplacement = mDisplacementY;
+        mLastY = ev.getY();
+        return true;
+    }
+
+    public void finishedScrolling() {
+        setState(ScrollState.IDLE);
+    }
+
+    private boolean reportDragStart(boolean recatch) {
+        mListener.onDragStart(!recatch);
+        if (DBG) {
+            Log.d(TAG, "onDragStart recatch:" + recatch);
+        }
+        return true;
+    }
+
+    private void initializeDragging() {
+        if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
+            mSubtractDisplacement = 0;
+        }
+        if (mDisplacementY > 0) {
+            mSubtractDisplacement = mTouchSlop;
+        } else {
+            mSubtractDisplacement = -mTouchSlop;
+        }
+    }
+
+    private boolean reportDragging() {
+        float delta = mDisplacementY - mLastDisplacement;
+        if (delta != 0) {
+            if (DBG) {
+                Log.d(TAG, String.format("onDrag disp=%.1f, velocity=%.1f",
+                        mDisplacementY, mVelocity));
+            }
+
+            return mListener.onDrag(mDisplacementY - mSubtractDisplacement, mVelocity);
+        }
+        return true;
+    }
+
+    private void reportDragEnd() {
+        if (DBG) {
+            Log.d(TAG, String.format("onScrolEnd disp=%.1f, velocity=%.1f",
+                    mDisplacementY, mVelocity));
+        }
+        mListener.onDragEnd(mVelocity, Math.abs(mVelocity) > RELEASE_VELOCITY_PX_MS);
+
+    }
+
+    /**
+     * Computes the damped velocity using the two motion events and the previous velocity.
+     */
+    private float computeVelocity(MotionEvent to) {
+        return computeVelocity(to.getY() - mLastY, to.getEventTime());
+    }
+
+    public float computeVelocity(float delta, long currentMillis) {
+        long previousMillis = mCurrentMillis;
+        mCurrentMillis = currentMillis;
+
+        float deltaTimeMillis = mCurrentMillis - previousMillis;
+        float velocity = (deltaTimeMillis > 0) ? (delta / deltaTimeMillis) : 0;
+        if (Math.abs(mVelocity) < 0.001f) {
+            mVelocity = velocity;
+        } else {
+            float alpha = computeDampeningFactor(deltaTimeMillis);
+            mVelocity = interpolate(mVelocity, velocity, alpha);
+        }
+        return mVelocity;
+    }
+
+    /**
+     * Returns a time-dependent dampening factor using delta time.
+     */
+    private static float computeDampeningFactor(float deltaTime) {
+        return deltaTime / (SCROLL_VELOCITY_DAMPENING_RC + deltaTime);
+    }
+
+    /**
+     * Returns the linear interpolation between two values
+     */
+    private static float interpolate(float from, float to, float alpha) {
+        return (1.0f - alpha) * from + alpha * to;
+    }
+}
diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
index ec1fb66..c7a529d 100644
--- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
+++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
@@ -1,113 +1,52 @@
 package com.android.launcher3.compat;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.icu.text.AlphabeticIndex;
+import android.os.Build;
+import android.os.LocaleList;
+import android.util.Log;
+
 import com.android.launcher3.Utilities;
 
-import java.lang.reflect.Constructor;
 import java.lang.reflect.Method;
 import java.util.Locale;
 
-/**
- * Fallback class to support Alphabetic indexing if not supported by the framework.
- * TODO(winsonc): disable for non-english locales
- */
-class BaseAlphabeticIndex {
-
-    private static final String BUCKETS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-";
-    private static final int UNKNOWN_BUCKET_INDEX = BUCKETS.length() - 1;
-
-    public BaseAlphabeticIndex() {}
-
-    /**
-     * Sets the max number of the label buckets in this index.
-     */
-    public void setMaxLabelCount(int count) {
-        // Not currently supported
-    }
-
-    /**
-     * Returns the index of the bucket in which the given string should appear.
-     */
-    protected int getBucketIndex(String s) {
-        if (s.isEmpty()) {
-            return UNKNOWN_BUCKET_INDEX;
-        }
-        int index = BUCKETS.indexOf(s.substring(0, 1).toUpperCase());
-        if (index != -1) {
-            return index;
-        }
-        return UNKNOWN_BUCKET_INDEX;
-    }
-
-    /**
-     * Returns the label for the bucket at the given index (as returned by getBucketIndex).
-     */
-    protected String getBucketLabel(int index) {
-        return BUCKETS.substring(index, index + 1);
-    }
-}
-
-/**
- * Reflected libcore.icu.AlphabeticIndex implementation, falls back to the base alphabetic index.
- */
-public class AlphabeticIndexCompat extends BaseAlphabeticIndex {
+public class AlphabeticIndexCompat {
+    private static final String TAG = "AlphabeticIndexCompat";
 
     private static final String MID_DOT = "\u2219";
-
-    private Object mAlphabeticIndex;
-    private Method mAddLabelsMethod;
-    private Method mSetMaxLabelCountMethod;
-    private Method mGetBucketIndexMethod;
-    private Method mGetBucketLabelMethod;
-    private boolean mHasValidAlphabeticIndex;
-    private String mDefaultMiscLabel;
+    private final BaseIndex mBaseIndex;
+    private final String mDefaultMiscLabel;
 
     public AlphabeticIndexCompat(Context context) {
-        super();
-        try {
-            Locale curLocale = context.getResources().getConfiguration().locale;
-            Class clazz = Class.forName("libcore.icu.AlphabeticIndex");
-            Constructor ctor = clazz.getConstructor(Locale.class);
-            mAddLabelsMethod = clazz.getDeclaredMethod("addLabels", Locale.class);
-            mSetMaxLabelCountMethod = clazz.getDeclaredMethod("setMaxLabelCount", int.class);
-            mGetBucketIndexMethod = clazz.getDeclaredMethod("getBucketIndex", String.class);
-            mGetBucketLabelMethod = clazz.getDeclaredMethod("getBucketLabel", int.class);
-            mAlphabeticIndex = ctor.newInstance(curLocale);
-            try {
-                // Ensure we always have some base English locale buckets
-                if (!curLocale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
-                    mAddLabelsMethod.invoke(mAlphabeticIndex, Locale.ENGLISH);
-                }
-            } catch (Exception e) {
-                e.printStackTrace();
-            }
-            if (curLocale.getLanguage().equals(Locale.JAPANESE.getLanguage())) {
-                // Japanese character 他 ("misc")
-                mDefaultMiscLabel = "\u4ed6";
-                // TODO(winsonc, omakoto): We need to handle Japanese sections better, especially the kanji
-            } else {
-                // Dot
-                mDefaultMiscLabel = MID_DOT;
-            }
-            mHasValidAlphabeticIndex = true;
-        } catch (Exception e) {
-            mHasValidAlphabeticIndex = false;
-        }
-    }
+        BaseIndex index = null;
 
-    /**
-     * Sets the max number of the label buckets in this index.
-     * (ICU 51 default is 99)
-     */
-    public void setMaxLabelCount(int count) {
-        if (mHasValidAlphabeticIndex) {
-            try {
-                mSetMaxLabelCountMethod.invoke(mAlphabeticIndex, count);
-            } catch (Exception e) {
-                e.printStackTrace();
+        try {
+            if (Utilities.isNycOrAbove()) {
+                index = new AlphabeticIndexVN(context);
             }
+        } catch (Exception e) {
+            Log.d(TAG, "Unable to load the system index", e);
+        }
+        if (index == null) {
+            try {
+                index = new AlphabeticIndexV16(context);
+            } catch (Exception e) {
+                Log.d(TAG, "Unable to load the system index", e);
+            }
+        }
+
+        mBaseIndex = index == null ? new BaseIndex() : index;
+
+        if (context.getResources().getConfiguration().locale
+                .getLanguage().equals(Locale.JAPANESE.getLanguage())) {
+            // Japanese character 他 ("misc")
+            mDefaultMiscLabel = "\u4ed6";
+            // TODO(winsonc, omakoto): We need to handle Japanese sections better, especially the kanji
         } else {
-            super.setMaxLabelCount(count);
+            // Dot
+            mDefaultMiscLabel = MID_DOT;
         }
     }
 
@@ -116,7 +55,7 @@
      */
     public String computeSectionName(CharSequence cs) {
         String s = Utilities.trim(cs);
-        String sectionName = getBucketLabel(getBucketIndex(s));
+        String sectionName = mBaseIndex.getBucketLabel(mBaseIndex.getBucketIndex(s));
         if (Utilities.trim(sectionName).isEmpty() && s.length() > 0) {
             int c = s.codePointAt(0);
             boolean startsWithDigit = Character.isDigit(c);
@@ -139,32 +78,120 @@
     }
 
     /**
-     * Returns the index of the bucket in which {@param s} should appear.
-     * Function is synchronized because underlying routine walks an iterator
-     * whose state is maintained inside the index object.
+     * Base class to support Alphabetic indexing if not supported by the framework.
+     * TODO(winsonc): disable for non-english locales
      */
-    protected int getBucketIndex(String s) {
-        if (mHasValidAlphabeticIndex) {
+    private static class BaseIndex {
+
+        private static final String BUCKETS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-";
+        private static final int UNKNOWN_BUCKET_INDEX = BUCKETS.length() - 1;
+
+        /**
+         * Returns the index of the bucket in which the given string should appear.
+         */
+        protected int getBucketIndex(String s) {
+            if (s.isEmpty()) {
+                return UNKNOWN_BUCKET_INDEX;
+            }
+            int index = BUCKETS.indexOf(s.substring(0, 1).toUpperCase());
+            if (index != -1) {
+                return index;
+            }
+            return UNKNOWN_BUCKET_INDEX;
+        }
+
+        /**
+         * Returns the label for the bucket at the given index (as returned by getBucketIndex).
+         */
+        protected String getBucketLabel(int index) {
+            return BUCKETS.substring(index, index + 1);
+        }
+    }
+
+    /**
+     * Reflected libcore.icu.AlphabeticIndex implementation, falls back to the base
+     * alphabetic index.
+     */
+    private static class AlphabeticIndexV16 extends BaseIndex {
+
+        private Object mAlphabeticIndex;
+        private Method mGetBucketIndexMethod;
+        private Method mGetBucketLabelMethod;
+
+        public AlphabeticIndexV16(Context context) throws Exception {
+            Locale curLocale = context.getResources().getConfiguration().locale;
+            Class clazz = Class.forName("libcore.icu.AlphabeticIndex");
+            mGetBucketIndexMethod = clazz.getDeclaredMethod("getBucketIndex", String.class);
+            mGetBucketLabelMethod = clazz.getDeclaredMethod("getBucketLabel", int.class);
+            mAlphabeticIndex = clazz.getConstructor(Locale.class).newInstance(curLocale);
+
+            if (!curLocale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
+                clazz.getDeclaredMethod("addLabels", Locale.class)
+                        .invoke(mAlphabeticIndex, Locale.ENGLISH);
+            }
+        }
+
+        /**
+         * Returns the index of the bucket in which {@param s} should appear.
+         * Function is synchronized because underlying routine walks an iterator
+         * whose state is maintained inside the index object.
+         */
+        protected int getBucketIndex(String s) {
             try {
                 return (Integer) mGetBucketIndexMethod.invoke(mAlphabeticIndex, s);
             } catch (Exception e) {
                 e.printStackTrace();
             }
+            return super.getBucketIndex(s);
         }
-        return super.getBucketIndex(s);
-    }
 
-    /**
-     * Returns the label for the bucket at the given index (as returned by getBucketIndex).
-     */
-    protected String getBucketLabel(int index) {
-        if (mHasValidAlphabeticIndex) {
+        /**
+         * Returns the label for the bucket at the given index (as returned by getBucketIndex).
+         */
+        protected String getBucketLabel(int index) {
             try {
                 return (String) mGetBucketLabelMethod.invoke(mAlphabeticIndex, index);
             } catch (Exception e) {
                 e.printStackTrace();
             }
+            return super.getBucketLabel(index);
         }
-        return super.getBucketLabel(index);
+    }
+
+    /**
+     * Implementation based on {@link AlphabeticIndex}.
+     */
+    @TargetApi(Build.VERSION_CODES.N)
+    private static class AlphabeticIndexVN extends BaseIndex {
+
+        private final AlphabeticIndex.ImmutableIndex mAlphabeticIndex;
+
+        public AlphabeticIndexVN(Context context) {
+            LocaleList locales = context.getResources().getConfiguration().getLocales();
+            int localeCount = locales.size();
+
+            Locale primaryLocale = localeCount == 0 ? Locale.ENGLISH : locales.get(0);
+            AlphabeticIndex indexBuilder = new AlphabeticIndex(primaryLocale);
+            for (int i = 1; i < localeCount; i++) {
+                indexBuilder.addLabels(locales.get(i));
+            }
+            indexBuilder.addLabels(Locale.ENGLISH);
+
+            mAlphabeticIndex = indexBuilder.buildImmutableIndex();
+        }
+
+        /**
+         * Returns the index of the bucket in which {@param s} should appear.
+         */
+        protected int getBucketIndex(String s) {
+            return mAlphabeticIndex.getBucketIndex(s);
+        }
+
+        /**
+         * Returns the label for the bucket at the given index
+         */
+        protected String getBucketLabel(int index) {
+            return mAlphabeticIndex.getBucket(index).getLabel();
+        }
     }
 }
diff --git a/src/com/android/launcher3/compat/DeferredLauncherActivityInfo.java b/src/com/android/launcher3/compat/DeferredLauncherActivityInfo.java
new file mode 100644
index 0000000..46d36d1
--- /dev/null
+++ b/src/com/android/launcher3/compat/DeferredLauncherActivityInfo.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+/**
+ * {@link LauncherActivityInfoCompat} which loads its data only when needed.
+ */
+public class DeferredLauncherActivityInfo extends LauncherActivityInfoCompat {
+
+    private final ComponentName mComponent;
+    private final UserHandleCompat mUser;
+    private final Context mContext;
+
+    private LauncherActivityInfoCompat mActualInfo;
+
+    public DeferredLauncherActivityInfo(
+            ComponentName component, UserHandleCompat user, Context context) {
+        mComponent = component;
+        mUser = user;
+        mContext = context;
+    }
+
+    @Override
+    public ComponentName getComponentName() {
+        return mComponent;
+    }
+
+    @Override
+    public UserHandleCompat getUser() {
+        return mUser;
+    }
+
+    private synchronized LauncherActivityInfoCompat getActualInfo() {
+        if (mActualInfo == null) {
+            Intent intent = new Intent(Intent.ACTION_MAIN)
+                    .addCategory(Intent.CATEGORY_LAUNCHER)
+                    .setComponent(mComponent);
+            mActualInfo = LauncherAppsCompat.getInstance(mContext).resolveActivity(intent, mUser);
+        }
+        return mActualInfo;
+    }
+
+    @Override
+    public CharSequence getLabel() {
+        return getActualInfo().getLabel();
+    }
+
+    @Override
+    public Drawable getIcon(int density) {
+        return getActualInfo().getIcon(density);
+    }
+
+    @Override
+    public ApplicationInfo getApplicationInfo() {
+        return getActualInfo().getApplicationInfo();
+    }
+
+    @Override
+    public long getFirstInstallTime() {
+        return getActualInfo().getFirstInstallTime();
+    }
+}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index 237a9e9..645e68a 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -19,24 +19,18 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherApps;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 
 import java.util.List;
 
 public abstract class LauncherAppsCompat {
 
-    public static final String ACTION_MANAGED_PROFILE_ADDED =
-            "android.intent.action.MANAGED_PROFILE_ADDED";
-    public static final String ACTION_MANAGED_PROFILE_REMOVED =
-            "android.intent.action.MANAGED_PROFILE_REMOVED";
-    public static final String ACTION_MANAGED_PROFILE_AVAILABLE =
-            "android.intent.action.MANAGED_PROFILE_AVAILABLE";
-    public static final String ACTION_MANAGED_PROFILE_UNAVAILABLE =
-            "android.intent.action.MANAGED_PROFILE_UNAVAILABLE";
-
     public interface OnAppsChangedCallbackCompat {
         void onPackageRemoved(String packageName, UserHandleCompat user);
         void onPackageAdded(String packageName, UserHandleCompat user);
@@ -45,6 +39,8 @@
         void onPackagesUnavailable(String[] packageNames, UserHandleCompat user, boolean replacing);
         void onPackagesSuspended(String[] packageNames, UserHandleCompat user);
         void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user);
+        void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
+                UserHandleCompat user);
     }
 
     protected LauncherAppsCompat() {
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
index 4e2fc05..49a0df6 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
@@ -22,15 +22,18 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
+import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.Settings;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Thunk;
 
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index 7270d02..d97bf2f 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -22,11 +22,14 @@
 import android.content.Intent;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
+import android.content.pm.ShortcutInfo;
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.UserHandle;
 
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -34,7 +37,7 @@
 import java.util.Map;
 
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class LauncherAppsCompatVL extends LauncherAppsCompat {
+public class LauncherAppsCompatVL extends LauncherAppsCompatV16 {
 
     protected LauncherApps mLauncherApps;
 
@@ -42,7 +45,7 @@
             = new HashMap<OnAppsChangedCallbackCompat, WrappedCallback>();
 
     LauncherAppsCompatVL(Context context) {
-        super();
+        super(context);
         mLauncherApps = (LauncherApps) context.getSystemService("launcherapps");
     }
 
@@ -146,6 +149,18 @@
         public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
             mCallback.onPackagesUnsuspended(packageNames, UserHandleCompat.fromUser(user));
         }
+
+        @Override
+        public void onShortcutsChanged(String packageName, List<ShortcutInfo> shortcuts,
+                UserHandle user) {
+            List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcuts.size());
+            for (ShortcutInfo shortcutInfo : shortcuts) {
+                shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
+            }
+
+            mCallback.onShortcutsChanged(packageName, shortcutInfoCompats,
+                    UserHandleCompat.fromUser(user));
+        }
     }
 }
 
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index 978f922..a5f8dd2 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -32,8 +32,12 @@
     public static UserManagerCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.isNycOrAbove()) {
+                if (Utilities.isNycMR1OrAbove()) {
+                    sInstance = new UserManagerCompatVNMr1(context.getApplicationContext());
+                } else if (Utilities.isNycOrAbove()) {
                     sInstance = new UserManagerCompatVN(context.getApplicationContext());
+                } else if (Utilities.ATLEAST_MARSHMALLOW) {
+                    sInstance = new UserManagerCompatVM(context.getApplicationContext());
                 } else if (Utilities.ATLEAST_LOLLIPOP) {
                     sInstance = new UserManagerCompatVL(context.getApplicationContext());
                 } else if (Utilities.ATLEAST_JB_MR1) {
@@ -57,4 +61,7 @@
     public abstract CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user);
     public abstract long getUserCreationTime(UserHandleCompat user);
     public abstract boolean isQuietModeEnabled(UserHandleCompat user);
+    public abstract boolean isUserUnlocked(UserHandleCompat user);
+
+    public abstract boolean isDemoUser();
 }
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java
index a006efd..9bd4567 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java
@@ -55,4 +55,14 @@
     public boolean isQuietModeEnabled(UserHandleCompat user) {
         return false;
     }
+
+    @Override
+    public boolean isUserUnlocked(UserHandleCompat user) {
+        return true;
+    }
+
+    @Override
+    public boolean isDemoUser() {
+        return false;
+    }
 }
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index c53d702..2552b0c 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (C) 2014 The Android Open Source Project
  *
@@ -94,9 +93,6 @@
 
     @Override
     public long getUserCreationTime(UserHandleCompat user) {
-        if (Utilities.ATLEAST_MARSHMALLOW) {
-            return mUserManager.getUserCreationTime(user.getUser());
-        }
         SharedPreferences prefs = Utilities.getPrefs(mContext);
         String key = USER_CREATION_TIME_KEY + getSerialNumberForUser(user);
         if (!prefs.contains(key)) {
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVM.java b/src/com/android/launcher3/compat/UserManagerCompatVM.java
new file mode 100644
index 0000000..81d67ea
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatVM.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+
+@TargetApi(Build.VERSION_CODES.M)
+public class UserManagerCompatVM extends UserManagerCompatVL {
+
+    UserManagerCompatVM(Context context) {
+        super(context);
+    }
+
+    @Override
+    public long getUserCreationTime(UserHandleCompat user) {
+        return mUserManager.getUserCreationTime(user.getUser());
+    }
+}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVN.java b/src/com/android/launcher3/compat/UserManagerCompatVN.java
index ae41e68..4edac05 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVN.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVN.java
@@ -16,18 +16,14 @@
 
 package com.android.launcher3.compat;
 
+import android.annotation.TargetApi;
 import android.content.Context;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
+import android.os.Build;
 
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
+import com.android.launcher3.Utilities;
 
-//TODO: Once gogole3 SDK is updated to N, add @TargetApi(Build.VERSION_CODES.N)
-public class UserManagerCompatVN extends UserManagerCompatVL {
-
-    private static final String TAG = "UserManagerCompatVN";
+@TargetApi(Build.VERSION_CODES.N)
+public class UserManagerCompatVN extends UserManagerCompatVM {
 
     UserManagerCompatVN(Context context) {
         super(context);
@@ -35,21 +31,12 @@
 
     @Override
     public boolean isQuietModeEnabled(UserHandleCompat user) {
-        if (user != null) {
-            try {
-                //TODO: Replace with proper API call once google3 SDK is updated.
-                Method isQuietModeEnabledMethod = UserManager.class.getMethod("isQuietModeEnabled",
-                        UserHandle.class);
-                return (boolean) isQuietModeEnabledMethod.invoke(mUserManager, user.getUser());
-            } catch (NoSuchMethodError | NoSuchMethodException | IllegalAccessException
-                    | InvocationTargetException e) {
-                Log.e(TAG, "Running on N without isQuietModeEnabled", e);
-            } catch (IllegalArgumentException e) {
-                // TODO remove this when API is fixed to not throw this
-                // when called on user that isn't a managed profile.
-            }
-        }
-        return false;
+        return mUserManager.isQuietModeEnabled(user.getUser());
+    }
+
+    @Override
+    public boolean isUserUnlocked(UserHandleCompat user) {
+        return mUserManager.isUserUnlocked(user.getUser());
     }
 }
 
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java b/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java
new file mode 100644
index 0000000..3f64bc8
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatVNMr1.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+
+@TargetApi(Build.VERSION_CODES.N_MR1)
+public class UserManagerCompatVNMr1 extends UserManagerCompatVN {
+
+    UserManagerCompatVNMr1(Context context) {
+        super(context);
+    }
+
+    @Override
+    public boolean isDemoUser() {
+        return mUserManager.isDemoUser();
+    }
+}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
deleted file mode 100644
index 5012708..0000000
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.config;
-
-/**
- * Defines a set of flags used to control various launcher behaviors
- * All the flags must be defined as
- *   public static boolean LAUNCHER3_FLAG_NAME = true/false;
- *
- * Use LAUNCHER3_ prefix for prevent namespace conflicts.
- */
-public final class FeatureFlags {
-
-    private FeatureFlags() {}
-
-    public static boolean IS_DEV_BUILD = false;
-    public static boolean IS_ALPHA_BUILD = false;
-    public static boolean IS_RELEASE_BUILD = true;
-
-    // Custom flags go below this
-    public static boolean LAUNCHER3_ICON_NORMALIZATION = false;
-
-}
diff --git a/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java b/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java
new file mode 100644
index 0000000..156941a
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/AnotherWindowDragSource.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dragndrop;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+
+/**
+ * DragSource used when the drag started at another window.
+ */
+public class AnotherWindowDragSource implements DragSource {
+
+    private final Context mContext;
+
+    AnotherWindowDragSource(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public boolean supportsFlingToDelete() {
+        return false;
+    }
+
+    @Override
+    public boolean supportsAppInfoDropTarget() {
+        return false;
+    }
+
+    @Override
+    public boolean supportsDeleteDropTarget() {
+        return false;
+    }
+
+    @Override
+    public float getIntrinsicIconScaleFactor() {
+        return 1;
+    }
+
+    @Override
+    public void onFlingToDeleteCompleted() {
+    }
+
+    @Override
+    public void onDropCompleted(View target, DragObject d,
+            boolean isFlingToDelete, boolean success) {
+        if (!success) {
+            Launcher.getLauncher(mContext).exitSpringLoadedDragModeDelayed(false, 0, null);
+        }
+
+    }
+
+    @Override
+    public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
+        // TODO: Probably log something
+    }
+}
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
similarity index 70%
rename from src/com/android/launcher3/DragController.java
rename to src/com/android/launcher3/dragndrop/DragController.java
index 2f0bfe5..6eb7dcc 100644
--- a/src/com/android/launcher3/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.dragndrop;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -25,34 +25,37 @@
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.IBinder;
-import android.util.Log;
+import android.view.DragEvent;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.accessibility.AccessibilityManager;
 import android.view.inputmethod.InputMethodManager;
 
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.TouchController;
 
 import java.util.ArrayList;
-import java.util.HashSet;
 
 /**
  * Class for initiating a drag within a view or across multiple views.
  */
-public class DragController {
+public class DragController implements DragDriver.EventListener, TouchController {
     private static final String TAG = "Launcher.DragController";
 
-    /** Indicates the drag is a move.  */
-    public static int DRAG_ACTION_MOVE = 0;
-
-    /** Indicates the drag is a copy.  */
-    public static int DRAG_ACTION_COPY = 1;
-
     public static final int SCROLL_DELAY = 500;
     public static final int RESCROLL_DELAY = PagedView.PAGE_SNAP_ANIMATION_DURATION + 150;
 
@@ -61,9 +64,9 @@
     private static final int SCROLL_OUTSIDE_ZONE = 0;
     private static final int SCROLL_WAITING_IN_ZONE = 1;
 
-    static final int SCROLL_NONE = -1;
-    static final int SCROLL_LEFT = 0;
-    static final int SCROLL_RIGHT = 1;
+    public static final int SCROLL_NONE = -1;
+    public static final int SCROLL_LEFT = 0;
+    public static final int SCROLL_RIGHT = 1;
 
     private static final float MAX_FLING_DEGREES = 35f;
 
@@ -75,11 +78,14 @@
     private final int[] mCoordinatesTemp = new int[2];
     private final boolean mIsRtl;
 
-    /** Whether or not we're dragging. */
-    private boolean mDragging;
+    /**
+     * Drag driver for the current drag/drop operation, or null if there is no active DND operation.
+     * It's null during accessible drag operations.
+     */
+    private DragDriver mDragDriver = null;
 
-    /** Whether or not this is an accessible drag operation */
-    private boolean mIsAccessibleDrag;
+    /** Options controlling the drag behavior. */
+    private DragOptions mOptions;
 
     /** X coordinate of the down event. */
     private int mMotionDownX;
@@ -90,7 +96,7 @@
     /** the area at the edge of the screen that makes the workspace go left
      *   or right while you're dragging.
      */
-    private int mScrollZone;
+    private final int mScrollZone;
 
     private DropTarget.DragObject mDragObject;
 
@@ -122,9 +128,11 @@
     private int mTmpPoint[] = new int[2];
     private Rect mDragLayerRect = new Rect();
 
-    protected int mFlingToDeleteThresholdVelocity;
+    protected final int mFlingToDeleteThresholdVelocity;
     private VelocityTracker mVelocityTracker;
 
+    private boolean mIsDragDeferred;
+
     /**
      * Interface to receive notifications when a drag starts or stops
      */
@@ -132,19 +140,17 @@
         /**
          * A drag has begun
          *
-         * @param source An object representing where the drag originated
-         * @param info The data associated with the object that is being dragged
-         * @param dragAction The drag action: either {@link DragController#DRAG_ACTION_MOVE}
-         *        or {@link DragController#DRAG_ACTION_COPY}
+         * @param dragObject The object being dragged
+         * @param options Options used to start the drag
          */
-        void onDragStart(DragSource source, Object info, int dragAction);
+        void onDragStart(DropTarget.DragObject dragObject, DragOptions options);
 
         /**
          * The drag has ended
          */
         void onDragEnd();
     }
-    
+
     /**
      * Used to create a new DragLayer from XML.
      */
@@ -155,16 +161,11 @@
         mScrollZone = r.getDimensionPixelSize(R.dimen.scroll_zone);
         mVelocityTracker = VelocityTracker.obtain();
 
-        float density = r.getDisplayMetrics().density;
         mFlingToDeleteThresholdVelocity =
-                (int) (r.getInteger(R.integer.config_flingToDeleteMinVelocity) * density);
+                r.getDimensionPixelSize(R.dimen.drag_flingToDeleteMinVelocity);
         mIsRtl = Utilities.isRtl(r);
     }
 
-    public boolean dragging() {
-        return mDragging;
-    }
-
     /**
      * Starts a drag.
      *
@@ -172,14 +173,10 @@
      * @param bmp The bitmap that represents the view being dragged
      * @param source An object representing where the drag originated
      * @param dragInfo The data associated with the object that is being dragged
-     * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
-     *        {@link #DRAG_ACTION_COPY}
      * @param viewImageBounds the position of the image inside the view
-     * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
-     *          Makes dragging feel more precise, e.g. you can clip out a transparent border
      */
-    public void startDrag(View v, Bitmap bmp, DragSource source, Object dragInfo,
-            Rect viewImageBounds, int dragAction, float initialDragViewScale) {
+    public void startDrag(View v, Bitmap bmp, DragSource source, ItemInfo dragInfo,
+            Rect viewImageBounds, float initialDragViewScale, DragOptions options) {
         int[] loc = mCoordinatesTemp;
         mLauncher.getDragLayer().getLocationInDragLayer(v, loc);
         int dragLayerX = loc[0] + viewImageBounds.left
@@ -187,12 +184,8 @@
         int dragLayerY = loc[1] + viewImageBounds.top
                 + (int) ((initialDragViewScale * bmp.getHeight() - bmp.getHeight()) / 2);
 
-        startDrag(bmp, dragLayerX, dragLayerY, source, dragInfo, dragAction, null,
-                null, initialDragViewScale, false);
-
-        if (dragAction == DRAG_ACTION_MOVE) {
-            v.setVisibility(View.GONE);
-        }
+        startDrag(bmp, dragLayerX, dragLayerY, source, dragInfo, null,
+                null, initialDragViewScale, options);
     }
 
     /**
@@ -204,15 +197,12 @@
      * @param dragLayerY The y position in the DragLayer of the left-top of the bitmap.
      * @param source An object representing where the drag originated
      * @param dragInfo The data associated with the object that is being dragged
-     * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
-     *        {@link #DRAG_ACTION_COPY}
      * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
      *          Makes dragging feel more precise, e.g. you can clip out a transparent border
-     * @param accessible whether this drag should occur in accessibility mode
      */
     public DragView startDrag(Bitmap b, int dragLayerX, int dragLayerY,
-            DragSource source, Object dragInfo, int dragAction, Point dragOffset, Rect dragRegion,
-            float initialDragViewScale, boolean accessible) {
+            DragSource source, ItemInfo dragInfo, Point dragOffset, Rect dragRegion,
+            float initialDragViewScale, DragOptions options) {
         if (PROFILE_DRAWING_DURING_DRAG) {
             android.os.Debug.startMethodTracing("Launcher");
         }
@@ -224,8 +214,10 @@
         }
         mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
 
-        for (DragListener listener : mListeners) {
-            listener.onDragStart(source, dragInfo, dragAction);
+        mOptions = options;
+        if (mOptions.systemDndStartPoint != null) {
+            mMotionDownX = mOptions.systemDndStartPoint.x;
+            mMotionDownY = mOptions.systemDndStartPoint.y;
         }
 
         final int registrationX = mMotionDownX - dragLayerX;
@@ -234,16 +226,23 @@
         final int dragRegionLeft = dragRegion == null ? 0 : dragRegion.left;
         final int dragRegionTop = dragRegion == null ? 0 : dragRegion.top;
 
-        mDragging = true;
-        mIsAccessibleDrag = accessible;
+        mLastDropTarget = null;
 
         mDragObject = new DropTarget.DragObject();
 
+        mIsDragDeferred = !mOptions.deferDragCondition.shouldStartDeferredDrag(0);
+
+        final Resources res = mLauncher.getResources();
+        final float scaleDps = FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND
+                ? res.getDimensionPixelSize(R.dimen.dragViewScale)
+                : mIsDragDeferred
+                    ? res.getDimensionPixelSize(R.dimen.deferred_drag_view_scale)
+                    : 0f;
         final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
-                registrationY, 0, 0, b.getWidth(), b.getHeight(), initialDragViewScale);
+                registrationY, initialDragViewScale, scaleDps);
 
         mDragObject.dragComplete = false;
-        if (mIsAccessibleDrag) {
+        if (mOptions.isAccessibleDrag) {
             // For an accessible drag, we assume the view is being dragged from the center.
             mDragObject.xOffset = b.getWidth() / 2;
             mDragObject.yOffset = b.getHeight() / 2;
@@ -252,10 +251,14 @@
             mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);
             mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
             mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
+
+            mDragDriver = DragDriver.create(mLauncher, this, mDragObject, mOptions);
         }
 
         mDragObject.dragSource = source;
         mDragObject.dragInfo = dragInfo;
+        mDragObject.originalDragInfo = new ItemInfo();
+        mDragObject.originalDragInfo.copyFrom(dragInfo);
 
         if (dragOffset != null) {
             dragView.setDragVisualizeOffset(new Point(dragOffset));
@@ -266,46 +269,31 @@
 
         mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
         dragView.show(mMotionDownX, mMotionDownY);
+        mDistanceSinceScroll = 0;
+
+        if (!mIsDragDeferred) {
+            startDeferredDrag();
+        } else {
+            mOptions.deferDragCondition.onDeferredDragStart();
+        }
+
+        mLastTouch[0] = mMotionDownX;
+        mLastTouch[1] = mMotionDownY;
         handleMoveEvent(mMotionDownX, mMotionDownY);
+        mLauncher.getUserEventDispatcher().resetActionDurationMillis();
         return dragView;
     }
 
-    /**
-     * Draw the view into a bitmap.
-     */
-    Bitmap getViewBitmap(View v) {
-        v.clearFocus();
-        v.setPressed(false);
+    public boolean isDeferringDrag() {
+        return mIsDragDeferred;
+    }
 
-        boolean willNotCache = v.willNotCacheDrawing();
-        v.setWillNotCacheDrawing(false);
-
-        // Reset the drawing cache background color to fully transparent
-        // for the duration of this operation
-        int color = v.getDrawingCacheBackgroundColor();
-        v.setDrawingCacheBackgroundColor(0);
-        float alpha = v.getAlpha();
-        v.setAlpha(1.0f);
-
-        if (color != 0) {
-            v.destroyDrawingCache();
+    public void startDeferredDrag() {
+        for (DragListener listener : new ArrayList<>(mListeners)) {
+            listener.onDragStart(mDragObject, mOptions);
         }
-        v.buildDrawingCache();
-        Bitmap cacheBitmap = v.getDrawingCache();
-        if (cacheBitmap == null) {
-            Log.e(TAG, "failed getViewBitmap(" + v + ")", new RuntimeException());
-            return null;
-        }
-
-        Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
-
-        // Restore the view
-        v.destroyDrawingCache();
-        v.setAlpha(alpha);
-        v.setWillNotCacheDrawing(willNotCache);
-        v.setDrawingCacheBackgroundColor(color);
-
-        return bitmap;
+        mOptions.deferDragCondition.onDragStart();
+        mIsDragDeferred = false;
     }
 
     /**
@@ -319,18 +307,22 @@
      * </pre>
      */
     public boolean dispatchKeyEvent(KeyEvent event) {
-        return mDragging;
+        return mDragDriver != null;
     }
 
     public boolean isDragging() {
-        return mDragging;
+        return mDragDriver != null || (mOptions != null && mOptions.isAccessibleDrag);
+    }
+
+    public boolean isExternalDrag() {
+        return (mOptions != null && mOptions.systemDndStartPoint != null);
     }
 
     /**
      * Stop dragging without dropping.
      */
     public void cancelDrag() {
-        if (mDragging) {
+        if (isDragging()) {
             if (mLastDropTarget != null) {
                 mLastDropTarget.onDragExit(mDragObject);
             }
@@ -342,31 +334,23 @@
         endDrag();
     }
 
-    public void onAppsRemoved(final HashSet<String> packageNames, HashSet<ComponentName> cns) {
+    public void onAppsRemoved(ItemInfoMatcher matcher) {
         // Cancel the current drag if we are removing an app that we are dragging
         if (mDragObject != null) {
-            Object rawDragInfo = mDragObject.dragInfo;
-            if (rawDragInfo instanceof ShortcutInfo) {
-                ShortcutInfo dragInfo = (ShortcutInfo) rawDragInfo;
-                for (ComponentName componentName : cns) {
-                    if (dragInfo.intent != null) {
-                        ComponentName cn = dragInfo.intent.getComponent();
-                        boolean isSameComponent = cn != null && (cn.equals(componentName) ||
-                                packageNames.contains(cn.getPackageName()));
-                        if (isSameComponent) {
-                            cancelDrag();
-                            return;
-                        }
-                    }
+            ItemInfo dragInfo = mDragObject.dragInfo;
+            if (dragInfo instanceof ShortcutInfo) {
+                ComponentName cn = dragInfo.getTargetComponent();
+                if (cn != null && matcher.matches(dragInfo, cn)) {
+                    cancelDrag();
                 }
             }
         }
     }
 
     private void endDrag() {
-        if (mDragging) {
-            mDragging = false;
-            mIsAccessibleDrag = false;
+        if (isDragging()) {
+            mDragDriver = null;
+            mOptions = null;
             clearScrollRunnable();
             boolean isDeferred = false;
             if (mDragObject.dragView != null) {
@@ -416,30 +400,64 @@
         return mTmpPoint;
     }
 
-    long getLastGestureUpTime() {
-        if (mDragging) {
+    public long getLastGestureUpTime() {
+        if (mDragDriver != null) {
             return System.currentTimeMillis();
         } else {
             return mLastTouchUpTime;
         }
     }
 
-    void resetLastGestureUpTime() {
+    public void resetLastGestureUpTime() {
         mLastTouchUpTime = -1;
     }
 
+    @Override
+    public void onDriverDragMove(float x, float y) {
+        final int[] dragLayerPos = getClampedDragLayerPos(x, y);
+
+        handleMoveEvent(dragLayerPos[0], dragLayerPos[1]);
+    }
+
+    @Override
+    public void onDriverDragExitWindow() {
+        if (mLastDropTarget != null) {
+            mLastDropTarget.onDragExit(mDragObject);
+            mLastDropTarget = null;
+        }
+    }
+
+    @Override
+    public void onDriverDragEnd(float x, float y, DropTarget dropTargetOverride) {
+        DropTarget dropTarget;
+        PointF vec = null;
+
+        if (dropTargetOverride != null) {
+            dropTarget = dropTargetOverride;
+        } else {
+            vec = isFlingingToDelete(mDragObject.dragSource);
+            if (vec != null) {
+                dropTarget = mFlingToDeleteDropTarget;
+            } else {
+                dropTarget = findDropTarget((int) x, (int) y, mCoordinatesTemp);
+            }
+        }
+
+        drop(dropTarget, x, y, vec);
+
+        endDrag();
+    }
+
+    @Override
+    public void onDriverDragCancel() {
+        cancelDrag();
+    }
+
     /**
      * Call this from a drag source view.
      */
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        @SuppressWarnings("all") // suppress dead code warning
-        final boolean debug = false;
-        if (debug) {
-            Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging="
-                    + mDragging);
-        }
-
-        if (mIsAccessibleDrag) {
+        if (mOptions != null && mOptions.isAccessibleDrag) {
             return false;
         }
 
@@ -452,43 +470,41 @@
         final int dragLayerY = dragLayerPos[1];
 
         switch (action) {
-            case MotionEvent.ACTION_MOVE:
-                break;
             case MotionEvent.ACTION_DOWN:
                 // Remember location of down touch
                 mMotionDownX = dragLayerX;
                 mMotionDownY = dragLayerY;
-                mLastDropTarget = null;
                 break;
             case MotionEvent.ACTION_UP:
                 mLastTouchUpTime = System.currentTimeMillis();
-                if (mDragging) {
-                    PointF vec = isFlingingToDelete(mDragObject.dragSource);
-                    if (!DeleteDropTarget.supportsDrop(mDragObject.dragInfo)) {
-                        vec = null;
-                    }
-                    if (vec != null) {
-                        dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
-                    } else {
-                        drop(dragLayerX, dragLayerY);
-                    }
-                }
-                endDrag();
-                break;
-            case MotionEvent.ACTION_CANCEL:
-                cancelDrag();
                 break;
         }
 
-        return mDragging;
+        return mDragDriver != null && mDragDriver.onInterceptTouchEvent(ev);
+    }
+
+    /**
+     * Call this from a drag source view.
+     */
+    public boolean onDragEvent(DragEvent event) {
+        return mDragDriver != null && mDragDriver.onDragEvent(event);
+    }
+
+    /**
+     * Call this from a drag view.
+     */
+    public void onDragViewAnimationEnd() {
+        if (mDragDriver != null) {
+            mDragDriver.onDragViewAnimationEnd();
+        }
     }
 
     /**
      * Sets the view that should handle move events.
      */
-    void setMoveTarget(View view) {
+    public void setMoveTarget(View view) {
         mMoveTarget = view;
-    }    
+    }
 
     public boolean dispatchUnhandledMove(View focused, int direction) {
         return mMoveTarget != null && mMoveTarget.dispatchUnhandledMove(focused, direction);
@@ -519,6 +535,15 @@
         mLastTouch[0] = x;
         mLastTouch[1] = y;
         checkScrollState(x, y);
+
+        if (mIsDragDeferred && mOptions.deferDragCondition.shouldStartDeferredDrag(
+                Math.hypot(x - mMotionDownX, y - mMotionDownY))) {
+            startDeferredDrag();
+        }
+    }
+
+    public float getDistanceDragged() {
+        return mDistanceSinceScroll;
     }
 
     public void forceTouchMove() {
@@ -557,7 +582,7 @@
             if (mScrollState == SCROLL_OUTSIDE_ZONE) {
                 mScrollState = SCROLL_WAITING_IN_ZONE;
                 if (mDragScroller.onEnterScrollArea(x, y, forwardDirection)) {
-                    dragLayer.onEnterScrollArea(forwardDirection);
+                    dragLayer.onEnterScrollArea();
                     mScrollRunnable.setDirection(forwardDirection);
                     mHandler.postDelayed(mScrollRunnable, delay);
                 }
@@ -566,7 +591,7 @@
             if (mScrollState == SCROLL_OUTSIDE_ZONE) {
                 mScrollState = SCROLL_WAITING_IN_ZONE;
                 if (mDragScroller.onEnterScrollArea(x, y, backwardsDirection)) {
-                    dragLayer.onEnterScrollArea(backwardsDirection);
+                    dragLayer.onEnterScrollArea();
                     mScrollRunnable.setDirection(backwardsDirection);
                     mHandler.postDelayed(mScrollRunnable, delay);
                 }
@@ -580,7 +605,7 @@
      * Call this from a drag source view.
      */
     public boolean onTouchEvent(MotionEvent ev) {
-        if (!mDragging || mIsAccessibleDrag) {
+        if (mDragDriver == null || mOptions == null || mOptions.isAccessibleDrag) {
             return false;
         }
 
@@ -593,47 +618,25 @@
         final int dragLayerY = dragLayerPos[1];
 
         switch (action) {
-        case MotionEvent.ACTION_DOWN:
-            // Remember where the motion event started
-            mMotionDownX = dragLayerX;
-            mMotionDownY = dragLayerY;
+            case MotionEvent.ACTION_DOWN:
+                // Remember where the motion event started
+                mMotionDownX = dragLayerX;
+                mMotionDownY = dragLayerY;
 
-            if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) {
-                mScrollState = SCROLL_WAITING_IN_ZONE;
-                mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
-            } else {
-                mScrollState = SCROLL_OUTSIDE_ZONE;
-            }
-            handleMoveEvent(dragLayerX, dragLayerY);
-            break;
-        case MotionEvent.ACTION_MOVE:
-            handleMoveEvent(dragLayerX, dragLayerY);
-            break;
-        case MotionEvent.ACTION_UP:
-            // Ensure that we've processed a move event at the current pointer location.
-            handleMoveEvent(dragLayerX, dragLayerY);
-            mHandler.removeCallbacks(mScrollRunnable);
-
-            if (mDragging) {
-                PointF vec = isFlingingToDelete(mDragObject.dragSource);
-                if (!DeleteDropTarget.supportsDrop(mDragObject.dragInfo)) {
-                    vec = null;
-                }
-                if (vec != null) {
-                    dropOnFlingToDeleteTarget(dragLayerX, dragLayerY, vec);
+                if ((dragLayerX < mScrollZone) || (dragLayerX > mScrollView.getWidth() - mScrollZone)) {
+                    mScrollState = SCROLL_WAITING_IN_ZONE;
+                    mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
                 } else {
-                    drop(dragLayerX, dragLayerY);
+                    mScrollState = SCROLL_OUTSIDE_ZONE;
                 }
-            }
-            endDrag();
-            break;
-        case MotionEvent.ACTION_CANCEL:
-            mHandler.removeCallbacks(mScrollRunnable);
-            cancelDrag();
-            break;
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mHandler.removeCallbacks(mScrollRunnable);
+                break;
         }
 
-        return true;
+        return mDragDriver.onTouchEvent(ev);
     }
 
     /**
@@ -643,7 +646,6 @@
     public void prepareAccessibleDrag(int x, int y) {
         mMotionDownX = x;
         mMotionDownY = y;
-        mLastDropTarget = null;
     }
 
     /**
@@ -661,7 +663,7 @@
 
         dropTarget.prepareAccessibilityDrop();
         // Perform the drop
-        drop(location[0], location[1]);
+        drop(dropTarget, location[0], location[1], null);
         endDrag();
     }
 
@@ -676,64 +678,68 @@
 
         ViewConfiguration config = ViewConfiguration.get(mLauncher);
         mVelocityTracker.computeCurrentVelocity(1000, config.getScaledMaximumFlingVelocity());
-
+        PointF vel = new PointF(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
+        float theta = MAX_FLING_DEGREES + 1;
         if (mVelocityTracker.getYVelocity() < mFlingToDeleteThresholdVelocity) {
             // Do a quick dot product test to ensure that we are flinging upwards
-            PointF vel = new PointF(mVelocityTracker.getXVelocity(),
-                    mVelocityTracker.getYVelocity());
             PointF upVec = new PointF(0f, -1f);
-            float theta = (float) Math.acos(((vel.x * upVec.x) + (vel.y * upVec.y)) /
-                    (vel.length() * upVec.length()));
-            if (theta <= Math.toRadians(MAX_FLING_DEGREES)) {
-                return vel;
-            }
+            theta = getAngleBetweenVectors(vel, upVec);
+        } else if (mLauncher.getDeviceProfile().isVerticalBarLayout() &&
+                mVelocityTracker.getXVelocity() < mFlingToDeleteThresholdVelocity) {
+            // Remove icon is on left side instead of top, so check if we are flinging to the left.
+            PointF leftVec = new PointF(-1f, 0f);
+            theta = getAngleBetweenVectors(vel, leftVec);
+        }
+        if (theta <= Math.toRadians(MAX_FLING_DEGREES)) {
+            return vel;
         }
         return null;
     }
 
-    private void dropOnFlingToDeleteTarget(float x, float y, PointF vel) {
-        final int[] coordinates = mCoordinatesTemp;
-
-        mDragObject.x = coordinates[0];
-        mDragObject.y = coordinates[1];
-
-        // Clean up dragging on the target if it's not the current fling delete target otherwise,
-        // start dragging to it.
-        if (mLastDropTarget != null && mFlingToDeleteDropTarget != mLastDropTarget) {
-            mLastDropTarget.onDragExit(mDragObject);
-        }
-
-        // Drop onto the fling-to-delete target
-        boolean accepted = false;
-        mFlingToDeleteDropTarget.onDragEnter(mDragObject);
-        // We must set dragComplete to true _only_ after we "enter" the fling-to-delete target for
-        // "drop"
-        mDragObject.dragComplete = true;
-        mFlingToDeleteDropTarget.onDragExit(mDragObject);
-        if (mFlingToDeleteDropTarget.acceptDrop(mDragObject)) {
-            mFlingToDeleteDropTarget.onFlingToDelete(mDragObject, vel);
-            accepted = true;
-        }
-        mDragObject.dragSource.onDropCompleted((View) mFlingToDeleteDropTarget, mDragObject, true,
-                accepted);
+    private float getAngleBetweenVectors(PointF vec1, PointF vec2) {
+        return (float) Math.acos(((vec1.x * vec2.x) + (vec1.y * vec2.y)) /
+                (vec1.length() * vec2.length()));
     }
 
-    private void drop(float x, float y) {
+    void drop(DropTarget dropTarget, float x, float y, PointF flingVel) {
         final int[] coordinates = mCoordinatesTemp;
-        final DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
 
         mDragObject.x = coordinates[0];
         mDragObject.y = coordinates[1];
+
+        // Move dragging to the final target.
+        if (dropTarget != mLastDropTarget) {
+            if (mLastDropTarget != null) {
+                mLastDropTarget.onDragExit(mDragObject);
+            }
+            mLastDropTarget = dropTarget;
+            if (dropTarget != null) {
+                dropTarget.onDragEnter(mDragObject);
+            }
+        }
+
+        mDragObject.dragComplete = true;
+
+        // Drop onto the target.
         boolean accepted = false;
         if (dropTarget != null) {
-            mDragObject.dragComplete = true;
             dropTarget.onDragExit(mDragObject);
             if (dropTarget.acceptDrop(mDragObject)) {
-                dropTarget.onDrop(mDragObject);
+                if (flingVel != null) {
+                    dropTarget.onFlingToDelete(mDragObject, flingVel);
+                } else {
+                    dropTarget.onDrop(mDragObject);
+                }
                 accepted = true;
             }
         }
-        mDragObject.dragSource.onDropCompleted((View) dropTarget, mDragObject, false, accepted);
+        final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null;
+        mDragObject.dragSource.onDropCompleted(
+                dropTargetAsView, mDragObject, flingVel != null, accepted);
+        mLauncher.getUserEventDispatcher().logDragNDrop(mDragObject, dropTargetAsView);
+        if (mIsDragDeferred) {
+            mOptions.deferDragCondition.onDropBeforeDeferredDrag();
+        }
     }
 
     private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {
@@ -826,10 +832,6 @@
         mScrollView = v;
     }
 
-    DragView getDragView() {
-        return mDragObject.dragView;
-    }
-
     private class ScrollRunnable implements Runnable {
         private int mDirection;
 
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
new file mode 100644
index 0000000..4db8c07
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dragndrop;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Context;
+import android.content.Intent;
+import android.view.DragEvent;
+import android.view.MotionEvent;
+
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.InstallShortcutReceiver;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+
+import java.util.ArrayList;
+
+/**
+ * Base class for driving a drag/drop operation.
+ */
+public abstract class DragDriver {
+    protected final EventListener mEventListener;
+
+    public interface EventListener {
+        void onDriverDragMove(float x, float y);
+        void onDriverDragExitWindow();
+        void onDriverDragEnd(float x, float y, DropTarget dropTargetOverride);
+        void onDriverDragCancel();
+    }
+
+    public DragDriver(EventListener eventListener) {
+        mEventListener = eventListener;
+    }
+
+    /**
+     * Handles ending of the DragView animation.
+     */
+    public void onDragViewAnimationEnd() { }
+
+    public boolean onTouchEvent(MotionEvent ev) {
+        final int action = ev.getAction();
+
+        switch (action) {
+            case MotionEvent.ACTION_MOVE:
+                mEventListener.onDriverDragMove(ev.getX(), ev.getY());
+                break;
+            case MotionEvent.ACTION_UP:
+                mEventListener.onDriverDragMove(ev.getX(), ev.getY());
+                mEventListener.onDriverDragEnd(ev.getX(), ev.getY(), null);
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                mEventListener.onDriverDragCancel();
+                break;
+        }
+
+        return true;
+    }
+
+    public abstract boolean onDragEvent (DragEvent event);
+
+
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        final int action = ev.getAction();
+
+        switch (action) {
+            case MotionEvent.ACTION_UP:
+                mEventListener.onDriverDragEnd(ev.getX(), ev.getY(), null);
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                mEventListener.onDriverDragCancel();
+                break;
+        }
+
+        return true;
+    }
+
+    public static DragDriver create(Context context, DragController dragController,
+            DragObject dragObject, DragOptions options) {
+        if (Utilities.isNycOrAbove() && options.systemDndStartPoint != null) {
+            return new SystemDragDriver(dragController, context, dragObject);
+        } else {
+            return new InternalDragDriver(dragController);
+        }
+    }
+}
+
+/**
+ * Class for driving a system (i.e. framework) drag/drop operation.
+ */
+class SystemDragDriver extends DragDriver {
+
+    private final DragObject mDragObject;
+    private final Context mContext;
+
+    boolean mReceivedDropEvent = false;
+    float mLastX = 0;
+    float mLastY = 0;
+
+    public SystemDragDriver(DragController dragController, Context context, DragObject dragObject) {
+        super(dragController);
+        mDragObject = dragObject;
+        mContext = context;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        return false;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        return false;
+    }
+
+    @Override
+    public boolean onDragEvent (DragEvent event) {
+        final int action = event.getAction();
+
+        switch (action) {
+            case DragEvent.ACTION_DRAG_STARTED:
+                mLastX = event.getX();
+                mLastY = event.getY();
+                return true;
+
+            case DragEvent.ACTION_DRAG_ENTERED:
+                return true;
+
+            case DragEvent.ACTION_DRAG_LOCATION:
+                mLastX = event.getX();
+                mLastY = event.getY();
+                mEventListener.onDriverDragMove(event.getX(), event.getY());
+                return true;
+
+            case DragEvent.ACTION_DROP:
+                mLastX = event.getX();
+                mLastY = event.getY();
+                mReceivedDropEvent =
+                        updateInfoFromClipData(event.getClipData(), event.getClipDescription());
+                return mReceivedDropEvent;
+
+            case DragEvent.ACTION_DRAG_EXITED:
+                mEventListener.onDriverDragExitWindow();
+                return true;
+
+            case DragEvent.ACTION_DRAG_ENDED:
+                if (mReceivedDropEvent) {
+                    mEventListener.onDriverDragEnd(mLastX, mLastY, null);
+                } else {
+                    mEventListener.onDriverDragCancel();
+                }
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    private boolean updateInfoFromClipData(ClipData data, ClipDescription desc) {
+        if (data == null) {
+            return false;
+        }
+        ArrayList<Intent> intents = new ArrayList<>();
+        int itemCount = data.getItemCount();
+        for (int i = 0; i < itemCount; i++) {
+            Intent intent = data.getItemAt(i).getIntent();
+            if (intent == null) {
+                continue;
+            }
+
+            // Give preference to shortcut intents.
+            if (!Intent.ACTION_CREATE_SHORTCUT.equals(intent.getAction())) {
+                intents.add(intent);
+                continue;
+            }
+            ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(mContext, intent);
+            if (info != null) {
+                mDragObject.dragInfo = info;
+                return true;
+            }
+            return true;
+        }
+
+        // Try creating shortcuts just using the intent and label
+        Intent fullIntent = new Intent().putExtra(Intent.EXTRA_SHORTCUT_NAME, desc.getLabel());
+        for (Intent intent : intents) {
+            fullIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, intent);
+            ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(mContext, fullIntent);
+            if (info != null) {
+                mDragObject.dragInfo = info;
+                return true;
+            }
+        }
+
+        return false;
+    }
+}
+
+/**
+ * Class for driving an internal (i.e. not using framework) drag/drop operation.
+ */
+class InternalDragDriver extends DragDriver {
+    public InternalDragDriver(DragController dragController) {
+        super(dragController);
+    }
+
+    @Override
+    public boolean onDragEvent (DragEvent event) { return false; }
+};
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
similarity index 75%
rename from src/com/android/launcher3/DragLayer.java
rename to src/com/android/launcher3/dragndrop/DragLayer.java
index 9886c3f..016347b 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -14,20 +14,30 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.dragndrop;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.TargetApi;
+import android.content.ClipDescription;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.view.DragEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
@@ -39,9 +49,30 @@
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.AppWidgetResizeFrame;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DropTargetBar;
+import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.InstallShortcutReceiver;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetHostView;
+import com.android.launcher3.PinchToOverviewListener;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.allapps.AllAppsTransitionController;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.keyboard.ViewGroupFocusHelper;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.TouchController;
 
+import java.net.URISyntaxException;
 import java.util.ArrayList;
 
 /**
@@ -76,10 +107,10 @@
 
     private boolean mHoverPointClosesFolder = false;
     private final Rect mHitRect = new Rect();
+    private final Rect mHighlightRect = new Rect();
 
     private TouchCompleteListener mTouchCompleteListener;
 
-    private View mOverlayView;
     private int mTopViewIndex;
     private int mChildCountOnLastUpdate = -1;
 
@@ -88,6 +119,8 @@
 
     // Related to adjacent page hints
     private final Rect mScrollChildPosition = new Rect();
+    private final ViewGroupFocusHelper mFocusIndicatorHelper;
+
     private boolean mInScrollArea;
     private boolean mShowPageHints;
     private Drawable mLeftHoverDrawable;
@@ -95,6 +128,13 @@
     private Drawable mLeftHoverDrawableActive;
     private Drawable mRightHoverDrawableActive;
 
+    // Related to pinch-to-go-to-overview gesture.
+    private PinchToOverviewListener mPinchListener = null;
+
+    // Handles all apps pull up interaction
+    private AllAppsTransitionController mAllAppsController;
+
+    private TouchController mActiveController;
     /**
      * Used to create a new DragLayer from XML.
      *
@@ -114,11 +154,22 @@
         mLeftHoverDrawableActive = res.getDrawable(R.drawable.page_hover_left_active);
         mRightHoverDrawableActive = res.getDrawable(R.drawable.page_hover_right_active);
         mIsRtl = Utilities.isRtl(res);
+        mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
     }
 
-    public void setup(Launcher launcher, DragController controller) {
+    public void setup(Launcher launcher, DragController dragController,
+            AllAppsTransitionController allAppsTransitionController) {
         mLauncher = launcher;
-        mDragController = controller;
+        mDragController = dragController;
+        mAllAppsController = allAppsTransitionController;
+
+        boolean isAccessibilityEnabled = ((AccessibilityManager) mLauncher.getSystemService(
+                Context.ACCESSIBILITY_SERVICE)).isEnabled();
+        onAccessibilityStateChanged(isAccessibilityEnabled);
+    }
+
+    public ViewGroupFocusHelper getFocusIndicatorHelper() {
+        return mFocusIndicatorHelper;
     }
 
     @Override
@@ -126,42 +177,35 @@
         return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
     }
 
-    public void showOverlayView(View overlayView) {
-        LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
-        mOverlayView = overlayView;
-        addView(overlayView, lp);
-
-        // 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.
-        mOverlayView.bringToFront();
+    public void onAccessibilityStateChanged(boolean isAccessibilityEnabled) {
+        mPinchListener = FeatureFlags.LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW || isAccessibilityEnabled
+                ? null : new PinchToOverviewListener(mLauncher);
     }
 
-    public void dismissOverlayView() {
-        removeView(mOverlayView);
+    public boolean isEventOverPageIndicator(MotionEvent ev) {
+        getDescendantRectRelativeToSelf(mLauncher.getWorkspace().getPageIndicator(), mHitRect);
+        return mHitRect.contains((int) ev.getX(), (int) ev.getY());
+    }
+
+    public boolean isEventOverHotseat(MotionEvent ev) {
+        return isEventOverView(mLauncher.getHotseat(), ev);
     }
 
     private boolean isEventOverFolderTextRegion(Folder folder, MotionEvent ev) {
-        getDescendantRectRelativeToSelf(folder.getEditTextRegion(), mHitRect);
-        if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
-            return true;
-        }
-        return false;
+        return isEventOverView(folder.getEditTextRegion(), ev);
     }
 
     private boolean isEventOverFolder(Folder folder, MotionEvent ev) {
-        getDescendantRectRelativeToSelf(folder, mHitRect);
-        if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
-            return true;
-        }
-        return false;
+        return isEventOverView(folder, ev);
     }
 
     private boolean isEventOverDropTargetBar(MotionEvent ev) {
-        getDescendantRectRelativeToSelf(mLauncher.getSearchDropTargetBar(), mHitRect);
-        if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
-            return true;
-        }
-        return false;
+        return isEventOverView(mLauncher.getDropTargetBar(), ev);
+    }
+
+    public boolean isEventOverView(View view, MotionEvent ev) {
+        getDescendantRectRelativeToSelf(view, mHitRect);
+        return mHitRect.contains((int) ev.getX(), (int) ev.getY());
     }
 
     private boolean handleTouchDown(MotionEvent ev, boolean intercept) {
@@ -182,6 +226,27 @@
             }
         }
 
+        // Remove the shortcuts container when touching outside of it.
+        DeepShortcutsContainer deepShortcutsContainer = mLauncher.getOpenShortcutsContainer();
+        if (deepShortcutsContainer != null) {
+            if (isEventOverView(deepShortcutsContainer, ev)) {
+                // Let the container handle the event.
+                return false;
+            } else {
+                if (isInAccessibleDrag()) {
+                    // Do not close the container if in drag and drop.
+                    if (!isEventOverDropTargetBar(ev)) {
+                        return true;
+                    }
+                } else {
+                    mLauncher.closeShortcutsContainer();
+                    // We let touches on the original icon go through so that users can launch
+                    // the app with one tap if they don't find a shortcut they want.
+                    return !isEventOverView(deepShortcutsContainer.getDeferredDragIcon(), ev);
+                }
+            }
+        }
+
         Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
         if (currentFolder != null && intercept) {
             if (currentFolder.isEditingName()) {
@@ -211,6 +276,10 @@
         int action = ev.getAction();
 
         if (action == MotionEvent.ACTION_DOWN) {
+            // Cancel discovery bounce animation when a user start interacting on anywhere on
+            // dray layer even if mAllAppsController is NOT the active controller.
+            // TODO: handle other input other than touch
+            mAllAppsController.cancelDiscoveryAnimation();
             if (handleTouchDown(ev, true)) {
                 return true;
             }
@@ -221,7 +290,25 @@
             mTouchCompleteListener = null;
         }
         clearAllResizeFrames();
-        return mDragController.onInterceptTouchEvent(ev);
+
+        mActiveController = null;
+
+        if (mDragController.onInterceptTouchEvent(ev)) {
+            mActiveController = mDragController;
+            return true;
+        }
+
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP && mAllAppsController.onInterceptTouchEvent(ev)) {
+            mActiveController = mAllAppsController;
+            return true;
+        }
+
+        if (mPinchListener != null && mPinchListener.onInterceptTouchEvent(ev)) {
+            // Stop listening for scrolling etc. (onTouchEvent() handles the rest of the pinch.)
+            mActiveController = mPinchListener;
+            return true;
+        }
+        return false;
     }
 
     @Override
@@ -267,37 +354,28 @@
     }
 
     private void sendTapOutsideFolderAccessibilityEvent(boolean isEditingName) {
-        AccessibilityManager accessibilityManager = (AccessibilityManager)
-                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-        if (accessibilityManager.isEnabled()) {
-            int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close;
-            AccessibilityEvent event = AccessibilityEvent.obtain(
-                    AccessibilityEvent.TYPE_VIEW_FOCUSED);
-            onInitializeAccessibilityEvent(event);
-            event.getText().add(getContext().getString(stringId));
-            accessibilityManager.sendAccessibilityEvent(event);
-        }
+        int stringId = isEditingName ? R.string.folder_tap_to_rename : R.string.folder_tap_to_close;
+        Utilities.sendCustomAccessibilityEvent(
+                this, AccessibilityEvent.TYPE_VIEW_FOCUSED, getContext().getString(stringId));
     }
 
     private boolean isInAccessibleDrag() {
-        LauncherAccessibilityDelegate delegate = LauncherAppState
-                .getInstance().getAccessibilityDelegate();
-        return delegate != null && delegate.isInAccessibleDrag();
+        return mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
     }
 
     @Override
     public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
-        Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
-        if (currentFolder != null) {
-            if (child == currentFolder) {
+        // Shortcuts can appear above folder
+        View topView = mLauncher.getTopFloatingView();
+        if (topView != null) {
+            if (child == topView) {
                 return super.onRequestSendAccessibilityEvent(child, event);
             }
-
-            if (isInAccessibleDrag() && child instanceof SearchDropTargetBar) {
+            if (isInAccessibleDrag() && child instanceof DropTargetBar) {
                 return super.onRequestSendAccessibilityEvent(child, event);
             }
-            // Skip propagating onRequestSendAccessibilityEvent all for other children
-            // when a folder is open
+            // Skip propagating onRequestSendAccessibilityEvent for all other children
+            // which are not topView
             return false;
         }
         return super.onRequestSendAccessibilityEvent(child, event);
@@ -305,13 +383,13 @@
 
     @Override
     public void addChildrenForAccessibility(ArrayList<View> childrenForAccessibility) {
-        Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
-        if (currentFolder != null) {
-            // Only add the folder as a child for accessibility when it is open
-            childrenForAccessibility.add(currentFolder);
+        View topView = mLauncher.getTopFloatingView();
+        if (topView != null) {
+            // Only add the top view as a child for accessibility when it is open
+            childrenForAccessibility.add(topView);
 
             if (isInAccessibleDrag()) {
-                childrenForAccessibility.add(mLauncher.getSearchDropTargetBar());
+                childrenForAccessibility.add(mLauncher.getDropTargetBar());
             }
         } else {
             super.addChildrenForAccessibility(childrenForAccessibility);
@@ -358,7 +436,53 @@
             }
         }
         if (handled) return true;
-        return mDragController.onTouchEvent(ev);
+        if (mActiveController != null) {
+            return mActiveController.onTouchEvent(ev);
+        }
+        return false;
+    }
+
+    @TargetApi(Build.VERSION_CODES.N)
+    private void handleSystemDragStart(DragEvent event) {
+        if (!FeatureFlags.LAUNCHER3_USE_SYSTEM_DRAG_DRIVER || !Utilities.isNycOrAbove()) {
+            return;
+        }
+        if (mLauncher.isWorkspaceLocked()) {
+            return;
+        }
+
+        ClipDescription description = event.getClipDescription();
+        if (!description.hasMimeType(ClipDescription.MIMETYPE_TEXT_INTENT)) {
+            return;
+        }
+        ShortcutInfo info = new ShortcutInfo();
+        // Set a dummy intent until we get the final value
+        info.intent = new Intent();
+
+        // Since we are not going through the workspace for starting the drag, set drag related
+        // information on the workspace before starting the drag.
+        ExternalDragPreviewProvider previewProvider =
+                new ExternalDragPreviewProvider(mLauncher, info);
+        mLauncher.getWorkspace().prepareDragWithProvider(previewProvider);
+
+        DragOptions options = new DragOptions();
+        options.systemDndStartPoint = new Point((int) event.getX(), (int) event.getY());
+
+        int halfPadding = previewProvider.previewPadding / 2;
+        mDragController.startDrag(
+                Bitmap.createBitmap(1, 1, Config.ARGB_8888),
+                0, 0,
+                new AnotherWindowDragSource(mLauncher), info,
+                new Point(- halfPadding, halfPadding),
+                previewProvider.getPreviewBounds(), 1f, options);
+    }
+
+    @Override
+    public boolean onDragEvent (DragEvent event) {
+        if (event.getAction() == DragEvent.ACTION_DRAG_STARTED) {
+            handleSystemDragStart(event);
+        }
+        return mDragController.onDragEvent(event);
     }
 
     /**
@@ -403,7 +527,7 @@
      */
     public float getDescendantCoordRelativeToSelf(View descendant, int[] coord,
             boolean includeRootScroll) {
-        return Utilities.getDescendantCoordRelativeToParent(descendant, this,
+        return Utilities.getDescendantCoordRelativeToAncestor(descendant, this,
                 coord, includeRootScroll);
     }
 
@@ -431,7 +555,9 @@
 
     @Override
     public boolean dispatchUnhandledMove(View focused, int direction) {
-        return mDragController.dispatchUnhandledMove(focused, direction);
+        // Consume the unhandled move if a container is open, to avoid switching pages underneath.
+        boolean isContainerOpen = mLauncher.getTopFloatingView() != null;
+        return isContainerOpen || mDragController.dispatchUnhandledMove(focused, direction);
     }
 
     @Override
@@ -656,13 +782,14 @@
      *        location doesn't account for scaling, and so should be centered about the desired
      *        final location (including scaling).
      * @param finalAlpha The final alpha of the view, in case we want it to fade as it animates.
-     * @param finalScale The final scale of the view. The view is scaled about its center.
+     * @param finalScaleX The final scale of the view. The view is scaled about its center.
+     * @param finalScaleY The final scale of the view. The view is scaled about its center.
      * @param duration The duration of the animation.
      * @param motionInterpolator The interpolator to use for the location of the view.
      * @param alphaInterpolator The interpolator to use for the alpha of the view.
      * @param onCompleteRunnable Optional runnable to run on animation completion.
-     * @param fadeOut Whether or not to fade out the view once the animation completes. If true,
-     *        the runnable will execute after the view is faded out.
+     * @param animationEndStyle Whether or not to fade out the view once the animation completes.
+     *        {@link #ANIMATION_END_DISAPPEAR} or {@link #ANIMATION_END_REMAIN_VISIBLE}.
      * @param anchorView If not null, this represents the view which the animated view stays
      *        anchored to in case scrolling is currently taking place. Note: currently this is
      *        only used for the X dimension for the case of the workspace.
@@ -680,11 +807,15 @@
 
         // If duration < 0, this is a cue to compute the duration based on the distance
         if (duration < 0) {
-            duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
-            if (dist < maxDist) {
-                duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
+            if (mDragController != null && mDragController.isExternalDrag()) {
+                duration = 1;
+            } else {
+                duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
+                if (dist < maxDist) {
+                    duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
+                }
+                duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
             }
-            duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
         }
 
         // Fall back to cubic ease out interpolator for the animation if none is specified
@@ -746,7 +877,7 @@
         // Show the drop view if it was previously hidden
         mDropView = view;
         mDropView.cancelAnimation();
-        mDropView.resetLayoutParams();
+        mDropView.requestLayout();
 
         // Set the anchor view if the page is scrolling
         if (anchorView != null) {
@@ -795,11 +926,6 @@
     @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.
-            mOverlayView.bringToFront();
-        }
         updateChildIndices();
     }
 
@@ -811,11 +937,6 @@
     @Override
     public void bringChildToFront(View child) {
         super.bringChildToFront(child);
-        if (child != mOverlayView && 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.
-            mOverlayView.bringToFront();
-        }
         updateChildIndices();
     }
 
@@ -856,7 +977,7 @@
         }
     }
 
-    void onEnterScrollArea(int direction) {
+    void onEnterScrollArea() {
         mInScrollArea = true;
         invalidate();
     }
@@ -866,7 +987,7 @@
         invalidate();
     }
 
-    void showPageHints() {
+    public void showPageHints() {
         mShowPageHints = true;
         Workspace workspace = mLauncher.getWorkspace();
         getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.numCustomPages()),
@@ -874,19 +995,37 @@
         invalidate();
     }
 
-    void hidePageHints() {
+    public void hidePageHints() {
         mShowPageHints = false;
         invalidate();
     }
 
+    public void invalidateScrim() {
+        if (mBackgroundAlpha > 0.0f) {
+            invalidate();
+        }
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
         // Draw the background below children.
         if (mBackgroundAlpha > 0.0f) {
+            // Update the scroll position first to ensure scrim cutout is in the right place.
+            mLauncher.getWorkspace().computeScrollWithoutInvalidation();
+
             int alpha = (int) (mBackgroundAlpha * 255);
+            CellLayout currCellLayout = mLauncher.getWorkspace().getCurrentDragOverlappingLayout();
+            canvas.save();
+            if (currCellLayout != null && currCellLayout != mLauncher.getHotseat().getLayout()) {
+                // Cut a hole in the darkening scrim on the page that should be highlighted, if any.
+                getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
+                canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
+            }
             canvas.drawColor((alpha << 24) | SCRIM_COLOR);
+            canvas.restore();
         }
 
+        mFocusIndicatorHelper.draw(canvas);
         super.dispatchDraw(canvas);
     }
 
@@ -936,6 +1075,26 @@
         return mBackgroundAlpha;
     }
 
+    @Override
+    protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+        View topView = mLauncher.getTopFloatingView();
+        if (topView != null) {
+            return topView.requestFocus(direction, previouslyFocusedRect);
+        } else {
+            return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
+        }
+    }
+
+    @Override
+    public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+        View topView = mLauncher.getTopFloatingView();
+        if (topView != null) {
+            topView.addFocusables(views, direction);
+        } else {
+            super.addFocusables(views, direction, focusableMode);
+        }
+    }
+
     public void setTouchCompleteListener(TouchCompleteListener listener) {
         mTouchCompleteListener = listener;
     }
diff --git a/src/com/android/launcher3/dragndrop/DragOptions.java b/src/com/android/launcher3/dragndrop/DragOptions.java
new file mode 100644
index 0000000..dbf46f3
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/DragOptions.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dragndrop;
+
+import android.graphics.Point;
+
+/**
+ * Set of options to control the drag and drop behavior.
+ */
+public class DragOptions {
+
+    /** Whether or not an accessible drag operation is in progress. */
+    public boolean isAccessibleDrag = false;
+
+    /** Specifies the start location for the system DnD, null when using internal DnD */
+    public Point systemDndStartPoint = null;
+
+    /** Determines when a deferred drag should start. By default, drags aren't deferred at all. */
+    public DeferDragCondition deferDragCondition = new DeferDragCondition();
+
+    /**
+     * Specifies a condition that must be met before DragListener#onDragStart() is called.
+     * By default, there is no condition and onDragStart() is called immediately following
+     * DragController#startDrag().
+     *
+     * This condition can be overridden, and callbacks are provided for the following cases:
+     * - The drag starts, but onDragStart() is deferred (onDeferredDragStart()).
+     * - The drag ends before the condition is met (onDropBeforeDeferredDrag()).
+     * - The condition is met (onDragStart()).
+     */
+    public static class DeferDragCondition {
+        public boolean shouldStartDeferredDrag(double distanceDragged) {
+            return true;
+        }
+
+        /**
+         * The drag has started, but onDragStart() is deferred.
+         * This happens when shouldStartDeferredDrag() returns true.
+         */
+        public void onDeferredDragStart() {
+            // Do nothing.
+        }
+
+        /**
+         * User dropped before the deferred condition was met,
+         * i.e. before shouldStartDeferredDrag() returned true.
+         */
+        public void onDropBeforeDeferredDrag() {
+            // Do nothing
+        }
+
+        /** onDragStart() has been called, now we are in a normal drag. */
+        public void onDragStart() {
+            // Do nothing
+        }
+    }
+}
diff --git a/src/com/android/launcher3/DragScroller.java b/src/com/android/launcher3/dragndrop/DragScroller.java
similarity index 96%
rename from src/com/android/launcher3/DragScroller.java
rename to src/com/android/launcher3/dragndrop/DragScroller.java
index e261f15..165d0b1 100644
--- a/src/com/android/launcher3/DragScroller.java
+++ b/src/com/android/launcher3/dragndrop/DragScroller.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.dragndrop;
 
 /**
  * Handles scrolling while dragging
diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
similarity index 73%
rename from src/com/android/launcher3/DragView.java
rename to src/com/android/launcher3/dragndrop/DragView.java
index a584667..8a2ae94 100644
--- a/src/com/android/launcher3/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -14,11 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.dragndrop;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.FloatArrayEvaluator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -33,31 +36,37 @@
 import android.view.View;
 import android.view.animation.DecelerateInterpolator;
 
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.Thunk;
 
+import com.android.launcher3.R;
+
 import java.util.Arrays;
 
 public class DragView extends View {
-    public static int COLOR_CHANGE_DURATION = 120;
+    public static final int COLOR_CHANGE_DURATION = 120;
+    public static final int VIEW_ZOOM_DURATION = 150;
 
     @Thunk static float sDragAlpha = 1f;
 
     private Bitmap mBitmap;
     private Bitmap mCrossFadeBitmap;
     @Thunk Paint mPaint;
-    private int mRegistrationX;
-    private int mRegistrationY;
+    private final int mRegistrationX;
+    private final int mRegistrationY;
 
     private Point mDragVisualizeOffset = null;
     private Rect mDragRegion = null;
-    private DragLayer mDragLayer = null;
+    private final DragLayer mDragLayer;
+    @Thunk final DragController mDragController;
     private boolean mHasDrawn = false;
     @Thunk float mCrossFadeProgress = 0f;
+    private boolean mAnimationCancelled = false;
 
     ValueAnimator mAnim;
-    @Thunk float mOffsetX = 0.0f;
-    @Thunk float mOffsetY = 0.0f;
-    private float mInitialScale = 1f;
     // The intrinsic icon scale factor is the scale factor for a drag icon over the workspace
     // size.  This is ignored for non-icons.
     private float mIntrinsicIconScale = 1f;
@@ -65,12 +74,16 @@
     @Thunk float[] mCurrentFilter;
     private ValueAnimator mFilterAnimator;
 
+    private int mLastTouchX;
+    private int mLastTouchY;
+    private int mAnimatedShiftX;
+    private int mAnimatedShiftY;
+
     /**
      * Construct the drag view.
      * <p>
      * The registration point is the point inside our view that the touch events should
      * be centered upon.
-     *
      * @param launcher The Launcher instance
      * @param bitmap The view that we're dragging around.  We scale it up when we draw it.
      * @param registrationX The x coordinate of the registration point.
@@ -78,14 +91,12 @@
      */
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     public DragView(Launcher launcher, Bitmap bitmap, int registrationX, int registrationY,
-            int left, int top, int width, int height, final float initialScale) {
+                    final float initialScale, final float finalScaleDps) {
         super(launcher);
         mDragLayer = launcher.getDragLayer();
-        mInitialScale = initialScale;
+        mDragController = launcher.getDragController();
 
-        final Resources res = getResources();
-        final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale);
-        final float scale = (width + scaleDps) / width;
+        final float scale = (bitmap.getWidth() + finalScaleDps) / bitmap.getWidth();
 
         // Set the initial scale to avoid any jumps
         setScaleX(initialScale);
@@ -93,17 +104,12 @@
 
         // Animate the view into the correct position
         mAnim = LauncherAnimUtils.ofFloat(this, 0f, 1f);
-        mAnim.setDuration(150);
+        mAnim.setDuration(VIEW_ZOOM_DURATION);
         mAnim.addUpdateListener(new AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 final float value = (Float) animation.getAnimatedValue();
 
-                final int deltaX = (int) (-mOffsetX);
-                final int deltaY = (int) (-mOffsetY);
-
-                mOffsetX += deltaX;
-                mOffsetY += deltaY;
                 setScaleX(initialScale + (value * (scale - initialScale)));
                 setScaleY(initialScale + (value * (scale - initialScale)));
                 if (sDragAlpha != 1f) {
@@ -112,15 +118,21 @@
 
                 if (getParent() == null) {
                     animation.cancel();
-                } else {
-                    setTranslationX(getTranslationX() + deltaX);
-                    setTranslationY(getTranslationY() + deltaY);
                 }
             }
         });
 
-        mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height);
-        setDragRegion(new Rect(0, 0, width, height));
+        mAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!mAnimationCancelled) {
+                    mDragController.onDragViewAnimationEnd();
+                }
+            }
+        });
+
+        mBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight());
+        setDragRegion(new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()));
 
         // The point in our scaled bitmap that the touch events are located
         mRegistrationX = registrationX;
@@ -145,10 +157,6 @@
         return mIntrinsicIconScale;
     }
 
-    public float getOffsetY() {
-        return mOffsetY;
-    }
-
     public int getDragRegionLeft() {
         return mDragRegion.left;
     }
@@ -181,30 +189,33 @@
         return mDragRegion;
     }
 
-    public float getInitialScale() {
-        return mInitialScale;
-    }
-
-    public void updateInitialScaleToCurrentScale() {
-        mInitialScale = getScaleX();
-    }
-
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         setMeasuredDimension(mBitmap.getWidth(), mBitmap.getHeight());
     }
 
+    // Draws drag shadow for system DND.
+    @SuppressLint("WrongCall")
+    public void drawDragShadow(Canvas canvas) {
+        final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+        canvas.scale(getScaleX(), getScaleY());
+        onDraw(canvas);
+        canvas.restoreToCount(saveCount);
+    }
+
+    // Provides drag shadow metrics for system DND.
+    public void provideDragShadowMetrics(Point size, Point touch) {
+        size.set((int)(mBitmap.getWidth() * getScaleX()), (int)(mBitmap.getHeight() * getScaleY()));
+
+        final float xGrowth = mBitmap.getWidth() * (getScaleX() - 1);
+        final float yGrowth = mBitmap.getHeight() * (getScaleY() - 1);
+        touch.set(
+                mRegistrationX + (int)Math.round(xGrowth / 2),
+                mRegistrationY + (int)Math.round(yGrowth / 2));
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
-        @SuppressWarnings("all") // suppress dead code warning
-        final boolean debug = false;
-        if (debug) {
-            Paint p = new Paint();
-            p.setStyle(Paint.Style.FILL);
-            p.setColor(0x66ffffff);
-            canvas.drawRect(0, 0, getWidth(), getHeight(), p);
-        }
-
         mHasDrawn = true;
         boolean crossFade = mCrossFadeProgress > 0 && mCrossFadeBitmap != null;
         if (crossFade) {
@@ -214,12 +225,12 @@
         canvas.drawBitmap(mBitmap, 0.0f, 0.0f, mPaint);
         if (crossFade) {
             mPaint.setAlpha((int) (255 * mCrossFadeProgress));
-            canvas.save();
+            final int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
             float sX = (mBitmap.getWidth() * 1.0f) / mCrossFadeBitmap.getWidth();
             float sY = (mBitmap.getHeight() * 1.0f) / mCrossFadeBitmap.getHeight();
             canvas.scale(sX, sY);
             canvas.drawBitmap(mCrossFadeBitmap, 0.0f, 0.0f, mPaint);
-            canvas.restore();
+            canvas.restoreToCount(saveCount);
         }
     }
 
@@ -235,6 +246,7 @@
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 mCrossFadeProgress = animation.getAnimatedFraction();
+                invalidate();
             }
         });
         va.start();
@@ -304,7 +316,6 @@
     /**
      * Create a window containing this view and show it.
      *
-     * @param windowToken obtained from v.getWindowToken() from one of your views
      * @param touchX the x coordinate the user touched in DragLayer coordinates
      * @param touchY the y coordinate the user touched in DragLayer coordinates
      */
@@ -317,8 +328,7 @@
         lp.height = mBitmap.getHeight();
         lp.customPosition = true;
         setLayoutParams(lp);
-        setTranslationX(touchX - mRegistrationX);
-        setTranslationY(touchY - mRegistrationY);
+        move(touchX, touchY);
         // Post the animation to skip other expensive work happening on the first frame
         post(new Runnable() {
             public void run() {
@@ -328,28 +338,48 @@
     }
 
     public void cancelAnimation() {
+        mAnimationCancelled = true;
         if (mAnim != null && mAnim.isRunning()) {
             mAnim.cancel();
         }
     }
 
-    public void resetLayoutParams() {
-        mOffsetX = mOffsetY = 0;
-        requestLayout();
-    }
-
     /**
      * Move the window containing this view.
      *
      * @param touchX the x coordinate the user touched in DragLayer coordinates
      * @param touchY the y coordinate the user touched in DragLayer coordinates
      */
-    void move(int touchX, int touchY) {
-        setTranslationX(touchX - mRegistrationX + (int) mOffsetX);
-        setTranslationY(touchY - mRegistrationY + (int) mOffsetY);
+    public void move(int touchX, int touchY) {
+        mLastTouchX = touchX;
+        mLastTouchY = touchY;
+        applyTranslation();
     }
 
-    void remove() {
+    public void animateShift(final int shiftX, final int shiftY) {
+        if (mAnim.isStarted()) {
+            return;
+        }
+        mAnimatedShiftX = shiftX;
+        mAnimatedShiftY = shiftY;
+        applyTranslation();
+        mAnim.addUpdateListener(new AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float fraction = 1 - animation.getAnimatedFraction();
+                mAnimatedShiftX = (int) (fraction * shiftX);
+                mAnimatedShiftY = (int) (fraction * shiftY);
+                applyTranslation();
+            }
+        });
+    }
+
+    private void applyTranslation() {
+        setTranslationX(mLastTouchX - mRegistrationX + mAnimatedShiftX);
+        setTranslationY(mLastTouchY - mRegistrationY + mAnimatedShiftY);
+    }
+
+    public void remove() {
         if (getParent() != null) {
             mDragLayer.removeView(DragView.this);
         }
diff --git a/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java b/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java
new file mode 100644
index 0000000..6b14be7
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/ExternalDragPreviewProvider.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dragndrop;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.HolographicOutlineHelper;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.graphics.DragPreviewProvider;
+
+/**
+ * Extension of {@link DragPreviewProvider} which provides a dummy outline when drag starts from
+ * a different window.
+ * It just draws an empty circle to a placeholder outline.
+ */
+public class ExternalDragPreviewProvider extends DragPreviewProvider {
+
+    private final Launcher mLauncher;
+    private final ItemInfo mAddInfo;
+
+    private final int[] mOutlineSize;
+
+    public ExternalDragPreviewProvider(Launcher launcher, ItemInfo addInfo) {
+        super(null);
+        mLauncher = launcher;
+        mAddInfo = addInfo;
+
+        mOutlineSize = mLauncher.getWorkspace().estimateItemSize(mAddInfo, false);
+    }
+
+    public Rect getPreviewBounds() {
+        Rect rect = new Rect();
+        DeviceProfile dp = mLauncher.getDeviceProfile();
+        rect.left = DRAG_BITMAP_PADDING / 2;
+        rect.top = (mOutlineSize[1] - dp.cellHeightPx) / 2;
+        rect.right = rect.left + dp.iconSizePx;
+        rect.bottom = rect.top + dp.iconSizePx;
+        return rect;
+    }
+
+    @Override
+    public Bitmap createDragOutline(Canvas canvas) {
+        final Bitmap b = Bitmap.createBitmap(mOutlineSize[0], mOutlineSize[1], Bitmap.Config.ALPHA_8);
+        canvas.setBitmap(b);
+
+        Paint paint = new Paint();
+        paint.setColor(Color.WHITE);
+        paint.setStyle(Paint.Style.FILL);
+
+        // Use 0.9f times the radius for the actual circle to account for icon normalization.
+        float radius = getPreviewBounds().width() * 0.5f;
+        canvas.drawCircle(DRAG_BITMAP_PADDING / 2 + radius,
+                DRAG_BITMAP_PADDING / 2 + radius, radius * 0.9f, paint);
+
+        HolographicOutlineHelper.obtain(mLauncher).applyExpensiveOutlineWithBlur(b, canvas);
+        canvas.setBitmap(null);
+        return b;
+    }
+}
diff --git a/src/com/android/launcher3/SpringLoadedDragController.java b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
similarity index 88%
rename from src/com/android/launcher3/SpringLoadedDragController.java
rename to src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
index 45edaef..37200a6 100644
--- a/src/com/android/launcher3/SpringLoadedDragController.java
+++ b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
@@ -14,13 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.dragndrop;
+
+import com.android.launcher3.Alarm;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.OnAlarmListener;
+import com.android.launcher3.Workspace;
 
 public class SpringLoadedDragController implements OnAlarmListener {
     // how long the user must hover over a mini-screen before it unshrinks
     final long ENTER_SPRING_LOAD_HOVER_TIME = 500;
     final long ENTER_SPRING_LOAD_CANCEL_HOVER_TIME = 950;
-    final long EXIT_SPRING_LOAD_HOVER_TIME = 200;
 
     Alarm mAlarm;
 
diff --git a/src/com/android/launcher3/dynamicui/ColorExtractionService.java b/src/com/android/launcher3/dynamicui/ColorExtractionService.java
new file mode 100644
index 0000000..1369f60
--- /dev/null
+++ b/src/com/android/launcher3/dynamicui/ColorExtractionService.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dynamicui;
+
+import android.app.IntentService;
+import android.app.WallpaperManager;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.support.v7.graphics.Palette;
+
+import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.config.FeatureFlags;
+
+/**
+ * Extracts colors from the wallpaper, and saves results to {@link LauncherProvider}.
+ */
+public class ColorExtractionService extends IntentService {
+
+    /** The fraction of the wallpaper to extract colors for use on the hotseat. */
+    private static final float HOTSEAT_FRACTION = 1f / 4;
+
+    public ColorExtractionService() {
+        super("ColorExtractionService");
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
+        int wallpaperId = ExtractionUtils.getWallpaperId(wallpaperManager);
+        ExtractedColors extractedColors = new ExtractedColors();
+        if (wallpaperManager.getWallpaperInfo() != null) {
+            // We can't extract colors from live wallpapers, so just use the default color always.
+            extractedColors.updatePalette(null);
+            extractedColors.updateHotseatPalette(null);
+        } else {
+            Bitmap wallpaper = ((BitmapDrawable) wallpaperManager.getDrawable()).getBitmap();
+            Palette palette = Palette.from(wallpaper).generate();
+            extractedColors.updatePalette(palette);
+            // We extract colors for the hotseat and status bar separately,
+            // since they only consider part of the wallpaper.
+            Palette hotseatPalette = Palette.from(wallpaper)
+                    .setRegion(0, (int) (wallpaper.getHeight() * (1f - HOTSEAT_FRACTION)),
+                            wallpaper.getWidth(), wallpaper.getHeight())
+                    .clearFilters()
+                    .generate();
+            extractedColors.updateHotseatPalette(hotseatPalette);
+
+            if (FeatureFlags.LIGHT_STATUS_BAR) {
+                int statusBarHeight = getResources()
+                        .getDimensionPixelSize(R.dimen.status_bar_height);
+                Palette statusBarPalette = Palette.from(wallpaper)
+                        .setRegion(0, 0, wallpaper.getWidth(), statusBarHeight)
+                        .clearFilters()
+                        .generate();
+                extractedColors.updateStatusBarPalette(statusBarPalette);
+            }
+        }
+
+        // Save the extracted colors and wallpaper id to LauncherProvider.
+        String colorsString = extractedColors.encodeAsString();
+        Bundle extras = new Bundle();
+        extras.putInt(LauncherSettings.Settings.EXTRA_WALLPAPER_ID, wallpaperId);
+        extras.putString(LauncherSettings.Settings.EXTRA_EXTRACTED_COLORS, colorsString);
+        getContentResolver().call(
+                LauncherSettings.Settings.CONTENT_URI,
+                LauncherSettings.Settings.METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID,
+                null, extras);
+    }
+}
diff --git a/src/com/android/launcher3/dynamicui/ExtractedColors.java b/src/com/android/launcher3/dynamicui/ExtractedColors.java
new file mode 100644
index 0000000..6a3011d
--- /dev/null
+++ b/src/com/android/launcher3/dynamicui/ExtractedColors.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dynamicui;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.support.v4.graphics.ColorUtils;
+import android.support.v7.graphics.Palette;
+import android.util.Log;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * Saves and loads colors extracted from the wallpaper, as well as the associated wallpaper id.
+ */
+public class ExtractedColors {
+    private static final String TAG = "ExtractedColors";
+
+    public static final int DEFAULT_LIGHT = Color.WHITE;
+    public static final int DEFAULT_DARK = Color.BLACK;
+    public static final int DEFAULT_COLOR = DEFAULT_LIGHT;
+
+    // These color profile indices should NOT be changed, since they are used when saving and
+    // loading extracted colors. New colors should always be added at the end.
+    public static final int VERSION_INDEX = 0;
+    public static final int HOTSEAT_INDEX = 1;
+    public static final int STATUS_BAR_INDEX = 2;
+    // public static final int VIBRANT_INDEX = 2;
+    // public static final int VIBRANT_DARK_INDEX = 3;
+    // public static final int VIBRANT_LIGHT_INDEX = 4;
+    // public static final int MUTED_INDEX = 5;
+    // public static final int MUTED_DARK_INDEX = 6;
+    // public static final int MUTED_LIGHT_INDEX = 7;
+
+    public static final int NUM_COLOR_PROFILES = 2;
+    private static final int VERSION = 1;
+
+    private static final String COLOR_SEPARATOR = ",";
+
+    private int[] mColors;
+
+    public ExtractedColors() {
+        // The first entry is reserved for the version number.
+        mColors = new int[NUM_COLOR_PROFILES + 1];
+        mColors[VERSION_INDEX] = VERSION;
+    }
+
+    public void setColorAtIndex(int index, int color) {
+        if (index > VERSION_INDEX && index < mColors.length) {
+            mColors[index] = color;
+        } else {
+            Log.e(TAG, "Attempted to set a color at an invalid index " + index);
+        }
+    }
+
+    /**
+     * Encodes {@link #mColors} as a comma-separated String.
+     */
+    String encodeAsString() {
+        StringBuilder colorsStringBuilder = new StringBuilder();
+        for (int color : mColors) {
+            colorsStringBuilder.append(color).append(COLOR_SEPARATOR);
+        }
+        return colorsStringBuilder.toString();
+    }
+
+    /**
+     * Decodes a comma-separated String into {@link #mColors}.
+     */
+    void decodeFromString(String colorsString) {
+        String[] splitColorsString = colorsString.split(COLOR_SEPARATOR);
+        mColors = new int[splitColorsString.length];
+        for (int i = 0; i < mColors.length; i++) {
+            mColors[i] = Integer.parseInt(splitColorsString[i]);
+        }
+    }
+
+    /**
+     * Loads colors and wallpaper id from {@link Utilities#getPrefs(Context)}.
+     * These were saved there in {@link ColorExtractionService}.
+     */
+    public void load(Context context) {
+        String encodedString = Utilities.getPrefs(context).getString(
+                ExtractionUtils.EXTRACTED_COLORS_PREFERENCE_KEY, VERSION + "");
+
+        decodeFromString(encodedString);
+
+        if (mColors[VERSION_INDEX] != VERSION) {
+            ExtractionUtils.startColorExtractionService(context);
+        }
+    }
+
+    /** @param index must be one of the index values defined at the top of this class. */
+    public int getColor(int index, int defaultColor) {
+        if (index > VERSION_INDEX && index < mColors.length) {
+            return mColors[index];
+        }
+        return defaultColor;
+    }
+
+    /**
+     * Updates colors based on the palette.
+     * If the palette is null, the default color is used in all cases.
+     */
+    public void updatePalette(Palette palette) {
+        if (palette == null) {
+            for (int i = 0; i < NUM_COLOR_PROFILES; i++) {
+                setColorAtIndex(i, ExtractedColors.DEFAULT_COLOR);
+            }
+        } else {
+            // We currently don't use any of the colors defined by the Palette API,
+            // but this is how we would add them if we ever need them.
+
+            // setColorAtIndex(ExtractedColors.VIBRANT_INDEX,
+                // palette.getVibrantColor(ExtractedColors.DEFAULT_COLOR));
+            // setColorAtIndex(ExtractedColors.VIBRANT_DARK_INDEX,
+                // palette.getDarkVibrantColor(ExtractedColors.DEFAULT_DARK));
+            // setColorAtIndex(ExtractedColors.VIBRANT_LIGHT_INDEX,
+                // palette.getLightVibrantColor(ExtractedColors.DEFAULT_LIGHT));
+            // setColorAtIndex(ExtractedColors.MUTED_INDEX,
+                // palette.getMutedColor(DEFAULT_COLOR));
+            // setColorAtIndex(ExtractedColors.MUTED_DARK_INDEX,
+                // palette.getDarkMutedColor(ExtractedColors.DEFAULT_DARK));
+            // setColorAtIndex(ExtractedColors.MUTED_LIGHT_INDEX,
+                // palette.getLightVibrantColor(ExtractedColors.DEFAULT_LIGHT));
+        }
+    }
+
+    /**
+     * The hotseat's color is defined as follows:
+     * - 12% black for super light wallpaper
+     * - 18% white for super dark
+     * - 25% white otherwise
+     */
+    public void updateHotseatPalette(Palette hotseatPalette) {
+        int hotseatColor;
+        if (hotseatPalette != null && ExtractionUtils.isSuperLight(hotseatPalette)) {
+            hotseatColor = ColorUtils.setAlphaComponent(Color.BLACK, (int) (0.12f * 255));
+        } else if (hotseatPalette != null && ExtractionUtils.isSuperDark(hotseatPalette)) {
+            hotseatColor = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.18f * 255));
+        } else {
+            hotseatColor = ColorUtils.setAlphaComponent(Color.WHITE, (int) (0.25f * 255));
+        }
+        setColorAtIndex(HOTSEAT_INDEX, hotseatColor);
+    }
+
+    public void updateStatusBarPalette(Palette statusBarPalette) {
+        setColorAtIndex(STATUS_BAR_INDEX, ExtractionUtils.isSuperLight(statusBarPalette) ?
+                DEFAULT_LIGHT : DEFAULT_DARK);
+    }
+}
diff --git a/src/com/android/launcher3/dynamicui/ExtractionUtils.java b/src/com/android/launcher3/dynamicui/ExtractionUtils.java
new file mode 100644
index 0000000..6dc0035
--- /dev/null
+++ b/src/com/android/launcher3/dynamicui/ExtractionUtils.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.dynamicui;
+
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Color;
+import android.support.v4.graphics.ColorUtils;
+import android.support.v7.graphics.Palette;
+
+import com.android.launcher3.Utilities;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * Contains helper fields and methods related to extracting colors from the wallpaper.
+ */
+public class ExtractionUtils {
+    public static final String EXTRACTED_COLORS_PREFERENCE_KEY = "pref_extractedColors";
+    public static final String WALLPAPER_ID_PREFERENCE_KEY = "pref_wallpaperId";
+
+    private static final int FLAG_SET_SYSTEM = 1 << 0; // TODO: use WallpaperManager.FLAG_SET_SYSTEM
+    private static final float MIN_CONTRAST_RATIO = 2f;
+
+    /**
+     * Extract colors in the :wallpaper-chooser process, if the wallpaper id has changed.
+     * When the new colors are saved in the LauncherProvider,
+     * Launcher will be notified in Launcher#onSettingsChanged(String, String).
+     */
+    public static void startColorExtractionServiceIfNecessary(final Context context) {
+        // Run on a background thread, since the service is asynchronous anyway.
+        Utilities.THREAD_POOL_EXECUTOR.execute(new Runnable() {
+            @Override
+            public void run() {
+                if (hasWallpaperIdChanged(context)) {
+                    startColorExtractionService(context);
+                }
+            }
+        });
+    }
+
+    /** Starts the {@link ColorExtractionService} without checking the wallpaper id */
+    public static void startColorExtractionService(Context context) {
+        context.startService(new Intent(context, ColorExtractionService.class));
+    }
+
+    private static boolean hasWallpaperIdChanged(Context context) {
+        if (!Utilities.isNycOrAbove()) {
+            // TODO: update an id in sharedprefs in onWallpaperChanged broadcast, and read it here.
+            return false;
+        }
+        final SharedPreferences sharedPrefs = Utilities.getPrefs(context);
+        int wallpaperId = getWallpaperId(WallpaperManager.getInstance(context));
+        int savedWallpaperId = sharedPrefs.getInt(ExtractionUtils.WALLPAPER_ID_PREFERENCE_KEY, -1);
+        return wallpaperId != savedWallpaperId;
+    }
+
+    public static int getWallpaperId(WallpaperManager wallpaperManager) {
+        // TODO: use WallpaperManager#getWallpaperId(WallpaperManager.FLAG_SET_SYSTEM) directly.
+        try {
+            Method getWallpaperId = WallpaperManager.class.getMethod("getWallpaperId", int.class);
+            return (int) getWallpaperId.invoke(wallpaperManager, FLAG_SET_SYSTEM);
+        } catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
+            return -1;
+        }
+    }
+
+    public static boolean isSuperLight(Palette p) {
+        return !isLegibleOnWallpaper(Color.WHITE, p.getSwatches());
+    }
+
+    public static boolean isSuperDark(Palette p) {
+        return !isLegibleOnWallpaper(Color.BLACK, p.getSwatches());
+    }
+
+    /**
+     * Given a color, returns true if that color is legible on
+     * the given wallpaper color swatches, else returns false.
+     */
+    private static boolean isLegibleOnWallpaper(int color, List<Palette.Swatch> wallpaperSwatches) {
+        int legiblePopulation = 0;
+        int illegiblePopulation = 0;
+        for (Palette.Swatch swatch : wallpaperSwatches) {
+            if (isLegible(color, swatch.getRgb())) {
+                legiblePopulation += swatch.getPopulation();
+            } else {
+                illegiblePopulation += swatch.getPopulation();
+            }
+        }
+        return legiblePopulation > illegiblePopulation;
+    }
+
+    /** @return Whether the foreground color is legible on the background color. */
+    private static boolean isLegible(int foreground, int background) {
+        background = ColorUtils.setAlphaComponent(background, 255);
+        return ColorUtils.calculateContrast(foreground, background) >= MIN_CONTRAST_RATIO;
+    }
+
+}
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
new file mode 100644
index 0000000..6ee02f9
--- /dev/null
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -0,0 +1,130 @@
+package com.android.launcher3.folder;
+
+import android.graphics.Path;
+import android.graphics.Point;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Utilities;
+
+public class ClippedFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule {
+
+    static final int MAX_NUM_ITEMS_IN_PREVIEW = 4;
+    private static final int MIN_NUM_ITEMS_IN_PREVIEW = 2;
+
+    final float MIN_SCALE = 0.48f;
+    final float MAX_SCALE = 0.58f;
+    final float MAX_RADIUS_DILATION = 0.15f;
+    final float ITEM_RADIUS_SCALE_FACTOR = 1.33f;
+
+    private float[] mTmpPoint = new float[2];
+
+    private float mAvailableSpace;
+    private float mRadius;
+    private float mIconSize;
+    private boolean mIsRtl;
+    private float mBaselineIconScale;
+
+    @Override
+    public void init(int availableSpace, int intrinsicIconSize, boolean rtl) {
+        mAvailableSpace = availableSpace;
+        mRadius = ITEM_RADIUS_SCALE_FACTOR * availableSpace / 2f;
+        mIconSize = intrinsicIconSize;
+        mIsRtl = rtl;
+        mBaselineIconScale = availableSpace / (intrinsicIconSize * 1f);
+    }
+
+    @Override
+    public FolderIcon.PreviewItemDrawingParams computePreviewItemDrawingParams(int index,
+            int curNumItems, FolderIcon.PreviewItemDrawingParams params) {
+
+        float totalScale = scaleForNumItems(curNumItems);
+        float transX;
+        float transY;
+        float overlayAlpha = 0;
+
+        // Items beyond those displayed in the preview are animated to the center
+        if (index >= MAX_NUM_ITEMS_IN_PREVIEW) {
+            transX = transY = mAvailableSpace / 2 - (mIconSize * totalScale) / 2;
+        } else {
+            getPosition(index, curNumItems, mTmpPoint);
+            transX = mTmpPoint[0];
+            transY = mTmpPoint[1];
+        }
+
+        if (params == null) {
+            params = new FolderIcon.PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
+        } else {
+            params.update(transX, transY, totalScale);
+            params.overlayAlpha = overlayAlpha;
+        }
+        return params;
+    }
+
+    private void getPosition(int index, int curNumItems, float[] result) {
+        // The case of two items is homomorphic to the case of one.
+        curNumItems = Math.max(curNumItems, 2);
+
+        // We model the preview as a circle of items starting in the appropriate piece of the
+        // upper left quadrant (to achieve horizontal and vertical symmetry).
+        double theta0 = mIsRtl ? 0 : Math.PI;
+
+        // In RTL we go counterclockwise
+        int direction = mIsRtl ? 1 : -1;
+
+        double thetaShift = 0;
+        if (curNumItems == 3) {
+            thetaShift = Math.PI / 6;
+        } else if (curNumItems == 4) {
+            thetaShift = Math.PI / 4;
+        }
+        theta0 += direction * thetaShift;
+
+        // We want the items to appear in reading order. For the case of 1, 2 and 3 items, this
+        // is natural for the circular model. With 4 items, however, we need to swap the 3rd and
+        // 4th indices to achieve reading order.
+        if (curNumItems == 4 && index == 3) {
+            index = 2;
+        } else if (curNumItems == 4 && index == 2) {
+            index = 3;
+        }
+
+        // We bump the radius up between 0 and MAX_RADIUS_DILATION % as the number of items increase
+        float radius = mRadius * (1 + MAX_RADIUS_DILATION * (curNumItems -
+                MIN_NUM_ITEMS_IN_PREVIEW) / (MAX_NUM_ITEMS_IN_PREVIEW - MIN_NUM_ITEMS_IN_PREVIEW));
+        double theta = theta0 + index * (2 * Math.PI / curNumItems) * direction;
+
+        float halfIconSize = (mIconSize * scaleForNumItems(curNumItems)) / 2;
+
+        // Map the location along the circle, and offset the coordinates to represent the center
+        // of the icon, and to be based from the top / left of the preview area. The y component
+        // is inverted to match the coordinate system.
+        result[0] = mAvailableSpace / 2 + (float) (radius * Math.cos(theta) / 2) - halfIconSize;
+        result[1] = mAvailableSpace / 2 + (float) (- radius * Math.sin(theta) / 2) - halfIconSize;
+
+    }
+
+    private float scaleForNumItems(int numItems) {
+        float scale = 1f;
+        if (numItems <= 2) {
+            scale = MAX_SCALE;
+        } else if (numItems == 3) {
+            scale = (MAX_SCALE + MIN_SCALE) / 2;
+        } else {
+            scale = MIN_SCALE;
+        }
+
+        return scale * mBaselineIconScale;
+    }
+
+    @Override
+    public int numItems() {
+        return MAX_NUM_ITEMS_IN_PREVIEW;
+    }
+
+    @Override
+    public boolean clipToBackground() {
+        return true;
+    }
+
+}
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/folder/Folder.java
similarity index 80%
rename from src/com/android/launcher3/Folder.java
rename to src/com/android/launcher3/folder/Folder.java
index a426215..698e5aa 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.folder;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -25,25 +25,23 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
-import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Build;
-import android.os.Bundle;
 import android.text.InputType;
 import android.text.Selection;
 import android.text.Spannable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.ActionMode;
+import android.view.FocusFinder;
 import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
+import android.view.ViewDebug;
 import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.AnimationUtils;
 import android.view.inputmethod.EditorInfo;
@@ -51,15 +49,40 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import com.android.launcher3.CellLayout.CellInfo;
-import com.android.launcher3.DragController.DragListener;
+import com.android.launcher3.Alarm;
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.FolderInfo;
 import com.android.launcher3.FolderInfo.FolderListener;
-import com.android.launcher3.UninstallDropTarget.UninstallSource;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LogDecelerateInterpolator;
+import com.android.launcher3.OnAlarmListener;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.UninstallDropTarget.DropTargetSource;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
+import com.android.launcher3.accessibility.AccessibileDragListenerAdapter;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragController.DragListener;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.pageindicators.PageIndicatorDots;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.util.CircleRevealOutlineProvider;
 import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.UiThreadCircularReveal;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -70,12 +93,11 @@
  */
 public class Folder extends LinearLayout implements DragSource, View.OnClickListener,
         View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
-        View.OnFocusChangeListener, DragListener, UninstallSource, AccessibilityDragSource,
-        Stats.LaunchSourceProvider {
+        View.OnFocusChangeListener, DragListener, DropTargetSource {
     private static final String TAG = "Launcher.Folder";
 
     /**
-     * We avoid measuring {@link #mContentWrapper} with a 0 width or height, as this
+     * We avoid measuring {@link #mContent} with a 0 width or height, as this
      * results in CellLayout being measured as UNSPECIFIED, which it does not support.
      */
     private static final int MIN_CONTENT_DIMEN = 5;
@@ -119,13 +141,13 @@
 
     protected final Launcher mLauncher;
     protected DragController mDragController;
-    protected FolderInfo mInfo;
+    public FolderInfo mInfo;
 
     @Thunk FolderIcon mFolderIcon;
 
     @Thunk FolderPagedView mContent;
-    @Thunk View mContentWrapper;
-    ExtendedEditText mFolderName;
+    public ExtendedEditText mFolderName;
+    private PageIndicatorDots mPageIndicator;
 
     private View mFooter;
     private int mFooterHeight;
@@ -133,13 +155,19 @@
     // Cell ranks used for drag and drop
     @Thunk int mTargetRank, mPrevTargetRank, mEmptyCellRank;
 
+    @ViewDebug.ExportedProperty(category = "launcher",
+            mapping = {
+                    @ViewDebug.IntToString(from = STATE_NONE, to = "STATE_NONE"),
+                    @ViewDebug.IntToString(from = STATE_SMALL, to = "STATE_SMALL"),
+                    @ViewDebug.IntToString(from = STATE_ANIMATING, to = "STATE_ANIMATING"),
+                    @ViewDebug.IntToString(from = STATE_OPEN, to = "STATE_OPEN"),
+            })
     @Thunk int mState = STATE_NONE;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mRearrangeOnClose = false;
     boolean mItemsInvalidated = false;
-    private ShortcutInfo mCurrentDragInfo;
     private View mCurrentDragView;
     private boolean mIsExternalDrag;
-    boolean mSuppressOnAdd = false;
     private boolean mDragInProgress = false;
     private boolean mDeleteFolderOnDropCompleted = false;
     private boolean mSuppressFolderDeletion = false;
@@ -148,6 +176,7 @@
     @Thunk float mFolderIconPivotY;
     private boolean mIsEditingName = false;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mDestroyed;
 
     @Thunk Runnable mDeferredAction;
@@ -183,7 +212,7 @@
         if (sHintText == null) {
             sHintText = res.getString(R.string.folder_hint_text);
         }
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
         // We need this view to be focusable in touch mode so that when text editing of the folder
         // name is complete, we have something to focus on, thus hiding the cursor and giving
         // reliable behavior when clicking the text field (since it will always gain focus on click).
@@ -193,10 +222,10 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mContentWrapper = findViewById(R.id.folder_content_wrapper);
         mContent = (FolderPagedView) findViewById(R.id.folder_content);
         mContent.setFolder(this);
 
+        mPageIndicator = (PageIndicatorDots) findViewById(R.id.folder_page_indicator);
         mFolderName = (ExtendedEditText) findViewById(R.id.folder_name);
         mFolderName.setOnBackKeyListener(new ExtendedEditText.OnBackKeyListener() {
             @Override
@@ -252,10 +281,20 @@
     public boolean onLongClick(View v) {
         // Return if global dragging is not enabled
         if (!mLauncher.isDraggingEnabled()) return true;
-        return beginDrag(v, false);
+        DragOptions dragOptions = new DragOptions();
+        if (v instanceof BubbleTextView) {
+            BubbleTextView icon = (BubbleTextView) v;
+            if (icon.hasDeepShortcuts()) {
+                DeepShortcutsContainer dsc = DeepShortcutsContainer.showForIcon(icon);
+                if (dsc != null) {
+                    dragOptions.deferDragCondition = dsc.createDeferDragCondition(null);
+                }
+            }
+        }
+        return startDrag(v, dragOptions);
     }
 
-    private boolean beginDrag(View v, boolean accessible) {
+    public boolean startDrag(View v, DragOptions options) {
         Object tag = v.getTag();
         if (tag instanceof ShortcutInfo) {
             ShortcutInfo item = (ShortcutInfo) tag;
@@ -263,35 +302,55 @@
                 return false;
             }
 
-            mLauncher.getWorkspace().beginDragShared(v, new Point(), this, accessible);
-
-            mCurrentDragInfo = item;
             mEmptyCellRank = item.rank;
             mCurrentDragView = v;
 
-            mContent.removeItem(mCurrentDragView);
-            mInfo.remove(mCurrentDragInfo);
-            mDragInProgress = true;
-            mItemAddedBackToSelfViaIcon = false;
+            mDragController.addDragListener(this);
+            if (options.isAccessibleDrag) {
+                mDragController.addDragListener(new AccessibileDragListenerAdapter(
+                        mContent, CellLayout.FOLDER_ACCESSIBILITY_DRAG) {
+
+                    @Override
+                    protected void enableAccessibleDrag(boolean enable) {
+                        super.enableAccessibleDrag(enable);
+                        mFooter.setImportantForAccessibility(enable
+                                ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
+                                : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+                    }
+                });
+            }
+
+            mLauncher.getWorkspace().beginDragShared(v, this, options);
         }
         return true;
     }
 
     @Override
-    public void startDrag(CellInfo cellInfo, boolean accessible) {
-        beginDrag(cellInfo.cell, accessible);
+    public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+        if (dragObject.dragSource != this) {
+            return;
+        }
+
+        mContent.removeItem(mCurrentDragView);
+        if (dragObject.dragInfo instanceof ShortcutInfo) {
+            mItemsInvalidated = true;
+
+            // We do not want to get events for the item being removed, as they will get handled
+            // when the drop completes
+            try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+                mInfo.remove((ShortcutInfo) dragObject.dragInfo, true);
+            }
+        }
+        mDragInProgress = true;
+        mItemAddedBackToSelfViaIcon = false;
     }
 
     @Override
-    public void enableAccessibleDrag(boolean enable) {
-        mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable);
-        for (int i = 0; i < mContent.getChildCount(); i++) {
-            mContent.getPageAt(i).enableAccessibleDrag(enable, CellLayout.FOLDER_ACCESSIBILITY_DRAG);
+    public void onDragEnd() {
+        if (mIsExternalDrag && mDragInProgress) {
+            completeDragExit();
         }
-
-        mFooter.setImportantForAccessibility(enable ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS :
-            IMPORTANT_FOR_ACCESSIBILITY_AUTO);
-        mLauncher.getWorkspace().setAddNewPageOnDrag(!enable);
+        mDragController.removeDragListener(this);
     }
 
     public boolean isEditingName() {
@@ -299,8 +358,13 @@
     }
 
     public void startEditingFolderName() {
-        mFolderName.setHint("");
-        mIsEditingName = true;
+        post(new Runnable() {
+            @Override
+            public void run() {
+                mFolderName.setHint("");
+                mIsEditingName = true;
+            }
+        });
     }
 
     public void dismissEditingName() {
@@ -317,8 +381,9 @@
         LauncherModel.updateItemInDatabase(mLauncher, mInfo);
 
         if (commit) {
-            sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
-                    String.format(getContext().getString(R.string.folder_renamed), newTitle));
+            Utilities.sendCustomAccessibilityEvent(
+                    this, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+                    getContext().getString(R.string.folder_renamed, newTitle));
         }
 
         // This ensures that focus is gained every time the field is clicked, which selects all
@@ -359,11 +424,26 @@
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        // requestFocus() causes the focus onto the folder itself, which doesn't cause visual
+        // effect but the next arrow key can start the keyboard focus inside of the folder, not
+        // the folder itself.
+        requestFocus();
+        super.onAttachedToWindow();
+    }
+
+    @Override
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
         // When the folder gets focus, we don't want to announce the list of items.
         return true;
     }
 
+    @Override
+    public View focusSearch(int direction) {
+        // When the folder is focused, further focus search should be within the folder contents.
+        return FocusFinder.getInstance().findNextFocus(this, null, direction);
+    }
+
     /**
      * @return the FolderInfo object associated with this folder
      */
@@ -381,8 +461,9 @@
         // If our folder has too many items we prune them from the list. This is an issue
         // when upgrading from the old Folders implementation which could contain an unlimited
         // number of items.
+        // TODO: Remove this, as with multi-page folders, there will never be any overflow
         for (ShortcutInfo item: overflow) {
-            mInfo.remove(item);
+            mInfo.remove(item, false);
             LauncherModel.deleteItemFromDatabase(mLauncher, item);
         }
 
@@ -424,8 +505,8 @@
     @SuppressLint("InflateParams")
     static Folder fromXml(Launcher launcher) {
         return (Folder) launcher.getLayoutInflater().inflate(
-                FeatureFlags.LAUNCHER3_ICON_NORMALIZATION
-                        ? R.layout.user_folder_icon_normalized : R.layout.user_folder, null);
+                FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION
+                        ? R.layout.user_folder : R.layout.user_folder_icon_normalized, null);
     }
 
     /**
@@ -457,7 +538,7 @@
         }
 
         // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
-        // leads to an consistent state if you drag out of the folder and drag back in without
+        // leads to an inconsistent state if you drag out of the folder and drag back in without
         // dropping. One resulting issue is that replaceFolderWithFinalItem() can be called twice.
         mDeleteFolderOnDropCompleted = false;
 
@@ -467,11 +548,7 @@
             positionAndSizeAsIcon();
             centerAboutIcon();
 
-            PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1);
-            PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
-            PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f);
-            final ObjectAnimator oa =
-                LauncherAnimUtils.ofPropertyValuesHolder(this, alpha, scaleX, scaleY);
+            final ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(this, 1, 1, 1);
             oa.setDuration(mExpandDuration);
             openFolderAnim = oa;
 
@@ -494,8 +571,8 @@
             float transY = - 0.075f * (height / 2 - getPivotY());
             setTranslationX(transX);
             setTranslationY(transY);
-            PropertyValuesHolder tx = PropertyValuesHolder.ofFloat("translationX", transX, 0);
-            PropertyValuesHolder ty = PropertyValuesHolder.ofFloat("translationY", transY, 0);
+            PropertyValuesHolder tx = PropertyValuesHolder.ofFloat(TRANSLATION_X, transX, 0);
+            PropertyValuesHolder ty = PropertyValuesHolder.ofFloat(TRANSLATION_Y, transY, 0);
 
             Animator drift = ObjectAnimator.ofPropertyValuesHolder(this, tx, ty);
             drift.setDuration(mMaterialExpandDuration);
@@ -506,13 +583,13 @@
             int ry = (int) Math.max(Math.max(height - getPivotY(), 0), getPivotY());
             float radius = (float) Math.hypot(rx, ry);
 
-            Animator reveal = UiThreadCircularReveal.createCircularReveal(this, (int) getPivotX(),
-                    (int) getPivotY(), 0, radius);
+            Animator reveal = new CircleRevealOutlineProvider((int) getPivotX(),
+                    (int) getPivotY(), 0, radius).createRevealAnimator(this);
             reveal.setDuration(mMaterialExpandDuration);
             reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
 
-            mContentWrapper.setAlpha(0f);
-            Animator iconsAlpha = ObjectAnimator.ofFloat(mContentWrapper, "alpha", 0f, 1f);
+            mContent.setAlpha(0f);
+            Animator iconsAlpha = ObjectAnimator.ofFloat(mContent, "alpha", 0f, 1f);
             iconsAlpha.setDuration(mMaterialExpandDuration);
             iconsAlpha.setStartDelay(mMaterialExpandStagger);
             iconsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
@@ -530,20 +607,23 @@
 
             openFolderAnim = anim;
 
-            mContentWrapper.setLayerType(LAYER_TYPE_HARDWARE, null);
+            mContent.setLayerType(LAYER_TYPE_HARDWARE, null);
             mFooter.setLayerType(LAYER_TYPE_HARDWARE, null);
             onCompleteRunnable = new Runnable() {
                 @Override
                 public void run() {
-                    mContentWrapper.setLayerType(LAYER_TYPE_NONE, null);
+                    mContent.setLayerType(LAYER_TYPE_NONE, null);
                     mFooter.setLayerType(LAYER_TYPE_NONE, null);
+                    mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
                 }
             };
         }
         openFolderAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
-                sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+                Utilities.sendCustomAccessibilityEvent(
+                        Folder.this,
+                        AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
                         mContent.getAccessibilityDescription());
                 mState = STATE_ANIMATING;
             }
@@ -564,13 +644,14 @@
             float textWidth =  mFolderName.getPaint().measureText(mFolderName.getText().toString());
             float translation = (footerWidth - textWidth) / 2;
             mFolderName.setTranslationX(mContent.mIsRtl ? -translation : translation);
-            mContent.setMarkerScale(0);
+            mPageIndicator.prepareEntryAnimation();
 
             // Do not update the flag if we are in drag mode. The flag will be updated, when we
             // actually drop the icon.
             final boolean updateAnimationFlag = !mDragInProgress;
             openFolderAnim.addListener(new AnimatorListenerAdapter() {
 
+                @SuppressLint("InlinedApi")
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     mFolderName.animate().setDuration(FOLDER_NAME_ANIMATION_DURATION)
@@ -579,7 +660,7 @@
                                 AnimationUtils.loadInterpolator(mLauncher,
                                         android.R.interpolator.fast_out_slow_in)
                                 : new LogDecelerateInterpolator(100, 0));
-                    mContent.animateMarkers();
+                    mPageIndicator.playEntryAnimation();
 
                     if (updateAnimationFlag) {
                         mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher);
@@ -588,9 +669,9 @@
             });
         } else {
             mFolderName.setTranslationX(0);
-            mContent.setMarkerScale(1);
         }
 
+        mPageIndicator.stopAllAnimations();
         openFolderAnim.start();
 
         // Make sure the folder picks up the last drag move even if the finger doesn't move.
@@ -598,13 +679,11 @@
             mDragController.forceTouchMove();
         }
 
-        FolderPagedView pages = (FolderPagedView) mContent;
-        pages.verifyVisibleHighResIcons(pages.getNextPage());
+        mContent.verifyVisibleHighResIcons(mContent.getNextPage());
     }
 
-    public void beginExternalDrag(ShortcutInfo item) {
-        mCurrentDragInfo = item;
-        mEmptyCellRank = mContent.allocateRankForNewItem(item);
+    public void beginExternalDrag() {
+        mEmptyCellRank = mContent.allocateRankForNewItem();
         mIsExternalDrag = true;
         mDragInProgress = true;
 
@@ -613,36 +692,9 @@
         mDragController.addDragListener(this);
     }
 
-    @Override
-    public void onDragStart(DragSource source, Object info, int dragAction) { }
-
-    @Override
-    public void onDragEnd() {
-        if (mIsExternalDrag && mDragInProgress) {
-            completeDragExit();
-        }
-        mDragController.removeDragListener(this);
-    }
-
-    @Thunk void sendCustomAccessibilityEvent(int type, String text) {
-        AccessibilityManager accessibilityManager = (AccessibilityManager)
-                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-        if (accessibilityManager.isEnabled()) {
-            AccessibilityEvent event = AccessibilityEvent.obtain(type);
-            onInitializeAccessibilityEvent(event);
-            event.getText().add(text);
-            accessibilityManager.sendAccessibilityEvent(event);
-        }
-    }
-
     public void animateClosed() {
         if (!(getParent() instanceof DragLayer)) return;
-        PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0);
-        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 0.9f);
-        PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 0.9f);
-        final ObjectAnimator oa =
-                LauncherAnimUtils.ofPropertyValuesHolder(this, alpha, scaleX, scaleY);
-
+        final ObjectAnimator oa = LauncherAnimUtils.ofViewAlphaAndScale(this, 0, 0.9f, 0.9f);
         oa.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -651,7 +703,9 @@
             }
             @Override
             public void onAnimationStart(Animator animation) {
-                sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+                Utilities.sendCustomAccessibilityEvent(
+                        Folder.this,
+                        AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
                         getContext().getString(R.string.folder_closed));
                 mState = STATE_ANIMATING;
             }
@@ -690,10 +744,11 @@
     }
 
     public boolean acceptDrop(DragObject d) {
-        final ItemInfo item = (ItemInfo) d.dragInfo;
+        final ItemInfo item = d.dragInfo;
         final int itemType = item.itemType;
         return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
-                    itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
+                itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
+                itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
                     !isFull());
     }
 
@@ -807,9 +862,7 @@
     }
 
     private void clearDragInfo() {
-        mCurrentDragInfo = null;
         mCurrentDragView = null;
-        mSuppressOnAdd = false;
         mIsExternalDrag = false;
     }
 
@@ -863,7 +916,7 @@
             if (mDeleteFolderOnDropCompleted && !mItemAddedBackToSelfViaIcon && target != this) {
                 replaceFolderWithFinalItem();
             }
-        } else {
+        } else if (!mDragController.isDeferringDrag()) {
             // The drag failed, we need to return the item to the folder
             ShortcutInfo info = (ShortcutInfo) d.dragInfo;
             View icon = (mCurrentDragView != null && mCurrentDragView.getTag() == info)
@@ -873,9 +926,9 @@
             mContent.arrangeChildren(views, views.size());
             mItemsInvalidated = true;
 
-            mSuppressOnAdd = true;
-            mFolderIcon.onDrop(d);
-            mSuppressOnAdd = false;
+            try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+                mFolderIcon.onDrop(d);
+            }
         }
 
         if (target != this) {
@@ -892,9 +945,7 @@
         mDeleteFolderOnDropCompleted = false;
         mDragInProgress = false;
         mItemAddedBackToSelfViaIcon = false;
-        mCurrentDragInfo = null;
         mCurrentDragView = null;
-        mSuppressOnAdd = false;
 
         // Reordering may have occured, and we need to save the new item locations. We do this once
         // at the end to prevent unnecessary database operations.
@@ -906,6 +957,12 @@
             // Show the animation, next time something is added to the folder.
             mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, false, mLauncher);
         }
+
+        if (!isFlingToDelete) {
+            // Fling to delete already exits spring loaded mode after the animation finishes.
+            mLauncher.exitSpringLoadedDragModeDelayed(successfulDrop,
+                    Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+        }
     }
 
     @Override
@@ -914,7 +971,7 @@
     }
 
     @Override
-    public void onUninstallActivityReturned(boolean success) {
+    public void onDragObjectRemoved(boolean success) {
         mDeferDropAfterUninstall = false;
         mUninstallSuccessful = success;
         if (mDeferredAction != null) {
@@ -934,7 +991,7 @@
 
     @Override
     public boolean supportsAppInfoDropTarget() {
-        return false;
+        return !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND;
     }
 
     @Override
@@ -965,16 +1022,6 @@
         LauncherModel.moveItemsInDatabase(mLauncher, items, mInfo.id, 0);
     }
 
-    public void addItemLocationsInDatabase() {
-        ArrayList<View> list = getItemsInReadingOrder();
-        for (int i = 0; i < list.size(); i++) {
-            View v = list.get(i);
-            ItemInfo info = (ItemInfo) v.getTag();
-            LauncherModel.addItemToDatabase(mLauncher, info, mInfo.id, 0,
-                    info.cellX, info.cellY);
-        }
-    }
-
     public void notifyDrop() {
         if (mDragInProgress) {
             mItemAddedBackToSelfViaIcon = true;
@@ -990,36 +1037,46 @@
     }
 
     private void centerAboutIcon() {
-        DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
 
+        DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
         DragLayer parent = (DragLayer) mLauncher.findViewById(R.id.drag_layer);
         int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
         int height = getFolderHeight();
 
         float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, sTempRect);
-
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-
-        int centerX = (int) (sTempRect.left + sTempRect.width() * scale / 2);
-        int centerY = (int) (sTempRect.top + sTempRect.height() * scale / 2);
+        int centerX = sTempRect.centerX();
+        int centerY = sTempRect.centerY();
         int centeredLeft = centerX - width / 2;
         int centeredTop = centerY - height / 2;
 
         // We need to bound the folder to the currently visible workspace area
         mLauncher.getWorkspace().getPageAreaRelativeToDragLayer(sTempRect);
         int left = Math.min(Math.max(sTempRect.left, centeredLeft),
-                sTempRect.left + sTempRect.width() - width);
+                sTempRect.right- width);
         int top = Math.min(Math.max(sTempRect.top, centeredTop),
-                sTempRect.top + sTempRect.height() - height);
-        if (grid.isPhone && (grid.availableWidthPx - width) < grid.iconSizePx) {
-            // Center the folder if it is full (on phones only)
+                sTempRect.bottom - height);
+
+        int distFromEdgeOfScreen = mLauncher.getWorkspace().getPaddingLeft() + getPaddingLeft();
+
+        if (grid.isPhone && (grid.availableWidthPx - width) < 4 * distFromEdgeOfScreen) {
+            // Center the folder if it is very close to being centered anyway, by virtue of
+            // filling the majority of the viewport. ie. remove it from the uncanny valley
+            // of centeredness.
             left = (grid.availableWidthPx - width) / 2;
         } else if (width >= sTempRect.width()) {
             // If the folder doesn't fit within the bounds, center it about the desired bounds
             left = sTempRect.left + (sTempRect.width() - width) / 2;
         }
         if (height >= sTempRect.height()) {
+            // Folder height is greater than page height, center on page
             top = sTempRect.top + (sTempRect.height() - height) / 2;
+        } else {
+            // Folder height is less than page height, so bound it to the absolute open folder
+            // bounds if necessary
+            Rect folderBounds = grid.getAbsoluteOpenFolderBounds();
+            left = Math.max(folderBounds.left, Math.min(left, folderBounds.right - width));
+            top = Math.max(folderBounds.top, Math.min(top, folderBounds.bottom - height));
         }
 
         int folderPivotX = width / 2 + (centeredLeft - left);
@@ -1037,19 +1094,17 @@
         lp.y = top;
     }
 
-    float getPivotXForIconAnimation() {
+    public float getPivotXForIconAnimation() {
         return mFolderIconPivotX;
     }
-    float getPivotYForIconAnimation() {
+    public float getPivotYForIconAnimation() {
         return mFolderIconPivotY;
     }
 
     private int getContentAreaHeight() {
         DeviceProfile grid = mLauncher.getDeviceProfile();
-        Rect workspacePadding = grid.getWorkspacePadding(mContent.mIsRtl);
-        int maxContentAreaHeight = grid.availableHeightPx -
-                workspacePadding.top - workspacePadding.bottom -
-                mFooterHeight;
+        int maxContentAreaHeight = grid.availableHeightPx
+                - grid.getTotalWorkspacePadding().y - mFooterHeight;
         int height = Math.min(maxContentAreaHeight,
                 mContent.getDesiredHeight());
         return Math.max(height, MIN_CONTENT_DIMEN);
@@ -1075,7 +1130,7 @@
         int contentAreaHeightSpec = MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY);
 
         mContent.setFixedSize(contentWidth, contentHeight);
-        mContentWrapper.measure(contentAreaWidthSpec, contentAreaHeightSpec);
+        mContent.measure(contentAreaWidthSpec, contentAreaHeightSpec);
 
         if (mContent.getChildCount() > 0) {
             int cellIconGap = (mContent.getPageAt(0).getCellWidth()
@@ -1111,11 +1166,6 @@
         mItemsInvalidated = true;
     }
 
-    // TODO remove this once GSA code fix is submitted
-    public ViewGroup getContent() {
-        return (ViewGroup) mContent;
-    }
-
     public int getItemCount() {
         return mContent.getItemCount();
     }
@@ -1168,7 +1218,7 @@
         mDestroyed = true;
     }
 
-    boolean isDestroyed() {
+    public boolean isDestroyed() {
         return mDestroyed;
     }
 
@@ -1237,7 +1287,14 @@
         mContent.completePendingPageChanges();
 
         View currentDragView;
-        ShortcutInfo si = mCurrentDragInfo;
+        final ShortcutInfo si;
+        if (d.dragInfo instanceof AppInfo) {
+            // Came from all apps -- make a copy.
+            si = ((AppInfo) d.dragInfo).makeShortcut();
+        } else {
+            // ShortcutInfo
+            si = (ShortcutInfo) d.dragInfo;
+        }
         if (mIsExternalDrag) {
             currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
             // Actually move the item in the database if it was an external drag. Call this
@@ -1252,7 +1309,9 @@
             mIsExternalDrag = false;
         } else {
             currentDragView = mCurrentDragView;
-            mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
+            if (!mDragController.isDeferringDrag()) {
+                mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
+            }
         }
 
         if (d.dragView.hasDrawn()) {
@@ -1273,18 +1332,24 @@
         mItemsInvalidated = true;
         rearrangeChildren();
 
-        // Temporarily suppress the listener, as we did all the work already here.
-        mSuppressOnAdd = true;
-        mInfo.add(si);
-        mSuppressOnAdd = false;
+        if (!mDragController.isDeferringDrag()) {
+            // Temporarily suppress the listener, as we did all the work already here.
+            try (SuppressInfoChanges s = new SuppressInfoChanges()) {
+                mInfo.add(si, false);
+            }
+        }
+
         // Clear the drag info, as it is no longer being dragged.
-        mCurrentDragInfo = null;
         mDragInProgress = false;
 
         if (mContent.getPageCount() > 1) {
             // The animation has already been shown while opening the folder.
             mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher);
         }
+
+        if (d.stateAnnouncer != null) {
+            d.stateAnnouncer.completeAction(R.string.item_moved);
+        }
     }
 
     // This is used so the item doesn't immediately appear in the folder when added. In one case
@@ -1301,10 +1366,7 @@
 
     @Override
     public void onAdd(ShortcutInfo item) {
-        // If the item was dropped onto this open folder, we have done the work associated
-        // with adding the item to the folder, as indicated by mSuppressOnAdd being set
-        if (mSuppressOnAdd) return;
-        mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem(item));
+        mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem());
         mItemsInvalidated = true;
         LauncherModel.addOrMoveItemInDatabase(
                 mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
@@ -1312,9 +1374,6 @@
 
     public void onRemove(ShortcutInfo item) {
         mItemsInvalidated = true;
-        // If this item is being dragged from this open folder, we have already handled
-        // the work associated with removing the item, so we don't have to do anything here.
-        if (item == mCurrentDragInfo) return;
         View v = getViewForInfo(item);
         mContent.removeItem(v);
         if (mState == STATE_ANIMATING) {
@@ -1335,13 +1394,14 @@
         return mContent.iterateOverItems(new ItemOperator() {
 
             @Override
-            public boolean evaluate(ItemInfo info, View view, View parent) {
+            public boolean evaluate(ItemInfo info, View view) {
                 return info == item;
             }
         });
     }
 
-    public void onItemsChanged() {
+    @Override
+    public void onItemsChanged(boolean animate) {
         updateTextViewFocus();
     }
 
@@ -1354,7 +1414,7 @@
             mContent.iterateOverItems(new ItemOperator() {
 
                 @Override
-                public boolean evaluate(ItemInfo info, View view, View parent) {
+                public boolean evaluate(ItemInfo info, View view) {
                     mItemsInReadingOrder.add(view);
                     return false;
                 }
@@ -1364,10 +1424,6 @@
         return mItemsInReadingOrder;
     }
 
-    public void getLocationInDragLayer(int[] loc) {
-        mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
-    }
-
     public void onFocusChange(View v, boolean hasFocus) {
         if (v == mFolderName) {
             if (hasFocus) {
@@ -1386,11 +1442,11 @@
     }
 
     @Override
-    public void fillInLaunchSourceData(View v, Bundle sourceData) {
-        // Fill in from the folder icon's launch source provider first
-        Stats.LaunchSourceUtils.populateSourceDataFromAncestorProvider(mFolderIcon, sourceData);
-        sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, Stats.SUB_CONTAINER_FOLDER);
-        sourceData.putInt(Stats.SOURCE_EXTRA_SUB_CONTAINER_PAGE, mContent.getCurrentPage());
+    public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
+        target.gridX = info.cellX;
+        target.gridY = info.cellY;
+        target.pageIndex = mContent.getCurrentPage();
+        targetParent.containerType = LauncherLogProto.FOLDER;
     }
 
     private class OnScrollHintListener implements OnAlarmListener {
@@ -1456,4 +1512,20 @@
             }
         }
     };
+
+    /**
+     * Temporary resource held while we don't want to handle info changes
+     */
+    private class SuppressInfoChanges implements AutoCloseable {
+
+        SuppressInfoChanges() {
+            mInfo.removeListener(Folder.this);
+        }
+
+        @Override
+        public void close() {
+            mInfo.addListener(Folder.this);
+            updateTextViewFocus();
+        }
+    }
 }
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
new file mode 100644
index 0000000..69c2b0f
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -0,0 +1,1002 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.folder;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.drawable.Drawable;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.Alarm;
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.CheckLongPressHelper;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.FolderInfo.FolderListener;
+import com.android.launcher3.IconCache;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.OnAlarmListener;
+import com.android.launcher3.PreloadIconDrawable;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.SimpleOnStylusPressListener;
+import com.android.launcher3.StylusEventHelper;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.util.Thunk;
+
+import java.util.ArrayList;
+
+/**
+ * An icon that can appear on in the workspace representing an {@link Folder}.
+ */
+public class FolderIcon extends FrameLayout implements FolderListener {
+    @Thunk Launcher mLauncher;
+    @Thunk Folder mFolder;
+    private FolderInfo mInfo;
+    @Thunk static boolean sStaticValuesDirty = true;
+
+    public static final int NUM_ITEMS_IN_PREVIEW = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ?
+            StackFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW :
+            ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
+
+    private CheckLongPressHelper mLongPressHelper;
+    private StylusEventHelper mStylusEventHelper;
+
+    // The number of icons to display in the
+    private static final int CONSUMPTION_ANIMATION_DURATION = 100;
+    private static final int DROP_IN_ANIMATION_DURATION = 400;
+    private static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
+    private static final int FINAL_ITEM_ANIMATION_DURATION = 200;
+
+    // Flag whether the folder should open itself when an item is dragged over is enabled.
+    public static final boolean SPRING_LOADING_ENABLED = true;
+
+    // Delay when drag enters until the folder opens, in miliseconds.
+    private static final int ON_OPEN_DELAY = 800;
+
+    @Thunk BubbleTextView mFolderName;
+
+    // These variables are all associated with the drawing of the preview; they are stored
+    // as member variables for shared usage and to avoid computation on each frame
+    private int mIntrinsicIconSize = -1;
+    private int mTotalWidth = -1;
+    private int mPrevTopPadding = -1;
+
+    PreviewBackground mBackground = new PreviewBackground();
+
+    private PreviewLayoutRule mPreviewLayoutRule;
+
+    boolean mAnimating = false;
+    private Rect mOldBounds = new Rect();
+
+    private float mSlop;
+
+    private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+    private ArrayList<PreviewItemDrawingParams> mDrawingParams = new ArrayList<PreviewItemDrawingParams>();
+    private Drawable mReferenceDrawable = null;
+
+    Paint mBgPaint = new Paint();
+
+    private Alarm mOpenAlarm = new Alarm();
+
+    public FolderIcon(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public FolderIcon(Context context) {
+        super(context);
+        init();
+    }
+
+    private void init() {
+        mLongPressHelper = new CheckLongPressHelper(this);
+        mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
+        mPreviewLayoutRule = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ?
+                new StackFolderIconLayoutRule() :
+                new ClippedFolderIconLayoutRule();
+    }
+
+    public static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
+            FolderInfo folderInfo, IconCache iconCache) {
+        @SuppressWarnings("all") // suppress dead code warning
+        final boolean error = INITIAL_ITEM_ANIMATION_DURATION >= DROP_IN_ANIMATION_DURATION;
+        if (error) {
+            throw new IllegalStateException("DROP_IN_ANIMATION_DURATION must be greater than " +
+                    "INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " +
+                    "is dependent on this");
+        }
+
+        DeviceProfile grid = launcher.getDeviceProfile();
+        FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
+
+        // For performance and compatibility reasons we render the preview using a software layer.
+        // In particular, hardware path clipping has spotty ecosystem support and bad performance.
+        // Software rendering also allows us to use shadow layers.
+        icon.setLayerType(LAYER_TYPE_SOFTWARE, new Paint(Paint.FILTER_BITMAP_FLAG));
+
+        icon.setClipToPadding(false);
+        icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
+        icon.mFolderName.setText(folderInfo.title);
+        icon.mFolderName.setCompoundDrawablePadding(0);
+        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams();
+        lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
+
+        icon.setTag(folderInfo);
+        icon.setOnClickListener(launcher);
+        icon.mInfo = folderInfo;
+        icon.mLauncher = launcher;
+        icon.setContentDescription(launcher.getString(R.string.folder_name_format, folderInfo.title));
+        Folder folder = Folder.fromXml(launcher);
+        folder.setDragController(launcher.getDragController());
+        folder.setFolderIcon(icon);
+        folder.bind(folderInfo);
+        icon.setFolder(folder);
+        icon.setAccessibilityDelegate(launcher.getAccessibilityDelegate());
+
+        folderInfo.addListener(icon);
+
+        icon.setOnFocusChangeListener(launcher.mFocusHandler);
+        return icon;
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        sStaticValuesDirty = true;
+        return super.onSaveInstanceState();
+    }
+
+    public Folder getFolder() {
+        return mFolder;
+    }
+
+    private void setFolder(Folder folder) {
+        mFolder = folder;
+        updateItemDrawingParams(false);
+    }
+
+    public FolderInfo getFolderInfo() {
+        return mInfo;
+    }
+
+    private boolean willAcceptItem(ItemInfo item) {
+        final int itemType = item.itemType;
+        return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
+                itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
+                itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) &&
+                !mFolder.isFull() && item != mInfo && !mInfo.opened);
+    }
+
+    public boolean acceptDrop(ItemInfo dragInfo) {
+        final ItemInfo item = dragInfo;
+        return !mFolder.isDestroyed() && willAcceptItem(item);
+    }
+
+    public void addItem(ShortcutInfo item) {
+        mInfo.add(item, true);
+    }
+
+    public void onDragEnter(ItemInfo dragInfo) {
+        if (mFolder.isDestroyed() || !willAcceptItem(dragInfo)) return;
+        CellLayout.LayoutParams lp = (CellLayout.LayoutParams) getLayoutParams();
+        CellLayout cl = (CellLayout) getParent().getParent();
+
+        mBackground.animateToAccept(cl, lp.cellX, lp.cellY);
+        mOpenAlarm.setOnAlarmListener(mOnOpenListener);
+        if (SPRING_LOADING_ENABLED &&
+                ((dragInfo instanceof AppInfo) || (dragInfo instanceof ShortcutInfo))) {
+            // TODO: we currently don't support spring-loading for PendingAddShortcutInfos even
+            // though widget-style shortcuts can be added to folders. The issue is that we need
+            // to deal with configuration activities which are currently handled in
+            // Workspace#onDropExternal.
+            mOpenAlarm.setAlarm(ON_OPEN_DELAY);
+        }
+    }
+
+    OnAlarmListener mOnOpenListener = new OnAlarmListener() {
+        public void onAlarm(Alarm alarm) {
+            mFolder.beginExternalDrag();
+            mLauncher.openFolder(FolderIcon.this);
+        }
+    };
+
+    public Drawable prepareCreate(final View destView) {
+        Drawable animateDrawable = getTopDrawable((TextView) destView);
+        computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
+                destView.getMeasuredWidth());
+        return animateDrawable;
+    }
+
+    public void performCreateAnimation(final ShortcutInfo destInfo, final View destView,
+            final ShortcutInfo srcInfo, final DragView srcView, Rect dstRect,
+            float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
+
+        // These correspond two the drawable and view that the icon was dropped _onto_
+        Drawable animateDrawable = prepareCreate(destView);
+
+        mReferenceDrawable = animateDrawable;
+
+        addItem(destInfo);
+        // This will animate the first item from it's position as an icon into its
+        // position as the first item in the preview
+        animateFirstItem(animateDrawable, INITIAL_ITEM_ANIMATION_DURATION, false, null);
+
+        // This will animate the dragView (srcView) into the new folder
+        onDrop(srcInfo, srcView, dstRect, scaleRelativeToDragLayer, 1, postAnimationRunnable);
+    }
+
+    public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) {
+        Drawable animateDrawable = getTopDrawable((TextView) finalView);
+        computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
+                finalView.getMeasuredWidth());
+
+        // This will animate the first item from it's position as an icon into its
+        // position as the first item in the preview
+        animateFirstItem(animateDrawable, FINAL_ITEM_ANIMATION_DURATION, true,
+                onCompleteRunnable);
+    }
+
+    public void onDragExit() {
+        mBackground.animateToRest();
+        mOpenAlarm.cancelAlarm();
+    }
+
+    private void onDrop(final ShortcutInfo item, DragView animateView, Rect finalRect,
+            float scaleRelativeToDragLayer, int index, Runnable postAnimationRunnable) {
+        item.cellX = -1;
+        item.cellY = -1;
+
+        // Typically, the animateView corresponds to the DragView; however, if this is being done
+        // after a configuration activity (ie. for a Shortcut being dragged from AllApps) we
+        // will not have a view to animate
+        if (animateView != null) {
+            DragLayer dragLayer = mLauncher.getDragLayer();
+            Rect from = new Rect();
+            dragLayer.getViewRectRelativeToSelf(animateView, from);
+            Rect to = finalRect;
+            if (to == null) {
+                to = new Rect();
+                Workspace workspace = mLauncher.getWorkspace();
+                // Set cellLayout and this to it's final state to compute final animation locations
+                workspace.setFinalTransitionTransform((CellLayout) getParent().getParent());
+                float scaleX = getScaleX();
+                float scaleY = getScaleY();
+                setScaleX(1.0f);
+                setScaleY(1.0f);
+                scaleRelativeToDragLayer = dragLayer.getDescendantRectRelativeToSelf(this, to);
+                // Finished computing final animation locations, restore current state
+                setScaleX(scaleX);
+                setScaleY(scaleY);
+                workspace.resetTransitionTransform((CellLayout) getParent().getParent());
+            }
+
+            int[] center = new int[2];
+            float scale = getLocalCenterForIndex(index, index + 1, center);
+            center[0] = (int) Math.round(scaleRelativeToDragLayer * center[0]);
+            center[1] = (int) Math.round(scaleRelativeToDragLayer * center[1]);
+
+            to.offset(center[0] - animateView.getMeasuredWidth() / 2,
+                      center[1] - animateView.getMeasuredHeight() / 2);
+
+            float finalAlpha = index < mPreviewLayoutRule.numItems() ? 0.5f : 0f;
+
+            float finalScale = scale * scaleRelativeToDragLayer;
+            dragLayer.animateView(animateView, from, to, finalAlpha,
+                    1, 1, finalScale, finalScale, DROP_IN_ANIMATION_DURATION,
+                    new DecelerateInterpolator(2), new AccelerateInterpolator(2),
+                    postAnimationRunnable, DragLayer.ANIMATION_END_DISAPPEAR, null);
+            addItem(item);
+            mFolder.hideItem(item);
+
+            final PreviewItemDrawingParams params = index < mDrawingParams.size() ?
+                    mDrawingParams.get(index) : null;
+            if (params != null) params.hidden = true;
+            postDelayed(new Runnable() {
+                public void run() {
+                    if (params != null) params.hidden = false;
+                    mFolder.showItem(item);
+                    invalidate();
+                }
+            }, DROP_IN_ANIMATION_DURATION);
+        } else {
+            addItem(item);
+        }
+    }
+
+    public void onDrop(DragObject d) {
+        ShortcutInfo item;
+        if (d.dragInfo instanceof AppInfo) {
+            // Came from all apps -- make a copy
+            item = ((AppInfo) d.dragInfo).makeShortcut();
+        } else {
+            item = (ShortcutInfo) d.dragInfo;
+        }
+        mFolder.notifyDrop();
+        onDrop(item, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable);
+    }
+
+    private void computePreviewDrawingParams(int drawableSize, int totalSize) {
+        if (mIntrinsicIconSize != drawableSize || mTotalWidth != totalSize ||
+                mPrevTopPadding != getPaddingTop()) {
+            DeviceProfile grid = mLauncher.getDeviceProfile();
+
+            mIntrinsicIconSize = drawableSize;
+            mTotalWidth = totalSize;
+            mPrevTopPadding = getPaddingTop();
+
+            mBackground.setup(getResources().getDisplayMetrics(), grid, this, mTotalWidth,
+                    getPaddingTop());
+            mPreviewLayoutRule.init(mBackground.previewSize, mIntrinsicIconSize,
+                    Utilities.isRtl(getResources()));
+
+            updateItemDrawingParams(false);
+        }
+    }
+
+    private void computePreviewDrawingParams(Drawable d) {
+        computePreviewDrawingParams(d.getIntrinsicWidth(), getMeasuredWidth());
+    }
+
+    static class PreviewItemDrawingParams {
+        PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
+            this.transX = transX;
+            this.transY = transY;
+            this.scale = scale;
+            this.overlayAlpha = overlayAlpha;
+        }
+
+        public void update(float transX, float transY, float scale) {
+            // We ensure the update will not interfere with an animation on the layout params
+            // If the final values differ, we cancel the animation.
+            if (anim != null) {
+                if (anim.finalTransX == transX || anim.finalTransY == transY
+                        || anim.finalScale == scale) {
+                    return;
+                }
+                anim.cancel();
+            }
+
+            this.transX = transX;
+            this.transY = transY;
+            this.scale = scale;
+        }
+
+        float transX;
+        float transY;
+        float scale;
+        public float overlayAlpha;
+        boolean hidden;
+        FolderPreviewItemAnim anim;
+        Drawable drawable;
+    }
+
+    private float getLocalCenterForIndex(int index, int curNumItems, int[] center) {
+        mTmpParams = computePreviewItemDrawingParams(Math.min(mPreviewLayoutRule.numItems(), index),
+                curNumItems, mTmpParams);
+
+        mTmpParams.transX += mBackground.basePreviewOffsetX;
+        mTmpParams.transY += mBackground.basePreviewOffsetY;
+        float offsetX = mTmpParams.transX + (mTmpParams.scale * mIntrinsicIconSize) / 2;
+        float offsetY = mTmpParams.transY + (mTmpParams.scale * mIntrinsicIconSize) / 2;
+
+        center[0] = (int) Math.round(offsetX);
+        center[1] = (int) Math.round(offsetY);
+        return mTmpParams.scale;
+    }
+
+    private PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
+            PreviewItemDrawingParams params) {
+        // We use an index of -1 to represent an icon on the workspace for the destroy and
+        // create animations
+        if (index == -1) {
+            return getFinalIconParams(params);
+        }
+        return mPreviewLayoutRule.computePreviewItemDrawingParams(index, curNumItems, params);
+    }
+
+    private PreviewItemDrawingParams getFinalIconParams(PreviewItemDrawingParams params) {
+        float iconSize = mLauncher.getDeviceProfile().iconSizePx;
+
+        final float scale = iconSize / mReferenceDrawable.getIntrinsicWidth();
+        final float trans = (mBackground.previewSize - iconSize) / 2;
+
+        params.update(trans, trans, scale);
+        return params;
+    }
+
+    private void drawPreviewItem(Canvas canvas, PreviewItemDrawingParams params) {
+        canvas.save();
+        canvas.translate(params.transX, params.transY);
+        canvas.scale(params.scale, params.scale);
+        Drawable d = params.drawable;
+
+        if (d != null) {
+            mOldBounds.set(d.getBounds());
+            d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
+            if (d instanceof FastBitmapDrawable) {
+                FastBitmapDrawable fd = (FastBitmapDrawable) d;
+                float oldBrightness = fd.getBrightness();
+                fd.setBrightness(params.overlayAlpha);
+                d.draw(canvas);
+                fd.setBrightness(oldBrightness);
+            } else {
+                d.setColorFilter(Color.argb((int) (params.overlayAlpha * 255), 255, 255, 255),
+                        PorterDuff.Mode.SRC_ATOP);
+                d.draw(canvas);
+                d.clearColorFilter();
+            }
+            d.setBounds(mOldBounds);
+        }
+        canvas.restore();
+    }
+
+    /**
+     * This object represents a FolderIcon preview background. It stores drawing / measurement
+     * information, handles drawing, and animation (accept state <--> rest state).
+     */
+    public static class PreviewBackground {
+        private float mScale = 1f;
+        private float mColorMultiplier = 1f;
+        private Path mClipPath = new Path();
+        private int mStrokeWidth;
+        private View mInvalidateDelegate;
+
+        public int previewSize;
+        private int basePreviewOffsetX;
+        private int basePreviewOffsetY;
+
+        private CellLayout mDrawingDelegate;
+        public int delegateCellX;
+        public int delegateCellY;
+
+        // When the PreviewBackground is drawn under an icon (for creating a folder) the border
+        // should not occlude the icon
+        public boolean isClipping = true;
+
+        // Drawing / animation configurations
+        private static final float ACCEPT_SCALE_FACTOR = 1.25f;
+        private static final float ACCEPT_COLOR_MULTIPLIER = 1.5f;
+
+        // Expressed on a scale from 0 to 255.
+        private static final int BG_OPACITY = 160;
+        private static final int MAX_BG_OPACITY = 225;
+        private static final int BG_INTENSITY = 245;
+        private static final int SHADOW_OPACITY = 80;
+
+        ValueAnimator mScaleAnimator;
+
+        public void setup(DisplayMetrics dm, DeviceProfile grid, View invalidateDelegate,
+                   int availableSpace, int topPadding) {
+            mInvalidateDelegate = invalidateDelegate;
+
+            final int previewSize = grid.folderIconSizePx;
+            final int previewPadding = grid.folderIconPreviewPadding;
+
+            this.previewSize = (previewSize - 2 * previewPadding);
+
+            basePreviewOffsetX = (availableSpace - this.previewSize) / 2;
+            basePreviewOffsetY = previewPadding + grid.folderBackgroundOffset + topPadding;
+
+            mStrokeWidth = Utilities.pxFromDp(1, dm);
+
+            invalidate();
+        }
+
+        int getRadius() {
+            return previewSize / 2;
+        }
+
+        int getScaledRadius() {
+            return (int) (mScale * getRadius());
+        }
+
+        int getOffsetX() {
+            return basePreviewOffsetX - (getScaledRadius() - getRadius());
+        }
+
+        int getOffsetY() {
+            return basePreviewOffsetY - (getScaledRadius() - getRadius());
+        }
+
+        void invalidate() {
+            int radius = getScaledRadius();
+            mClipPath.reset();
+            mClipPath.addCircle(radius, radius, radius, Path.Direction.CW);
+
+            if (mInvalidateDelegate != null) {
+                mInvalidateDelegate.invalidate();
+            }
+
+            if (mDrawingDelegate != null) {
+                mDrawingDelegate.invalidate();
+            }
+        }
+
+        void setInvalidateDelegate(View invalidateDelegate) {
+            mInvalidateDelegate = invalidateDelegate;
+            invalidate();
+        }
+
+        public void drawBackground(Canvas canvas, Paint paint) {
+            canvas.save();
+            canvas.translate(getOffsetX(), getOffsetY());
+
+            paint.reset();
+            paint.setStyle(Paint.Style.FILL);
+            paint.setXfermode(null);
+            paint.setAntiAlias(true);
+
+            int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
+            paint.setColor(Color.argb(alpha, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
+
+            float radius = getScaledRadius();
+
+            canvas.drawCircle(radius, radius, radius, paint);
+            canvas.clipPath(mClipPath, Region.Op.DIFFERENCE);
+
+            paint.setStyle(Paint.Style.STROKE);
+            paint.setColor(Color.TRANSPARENT);
+            paint.setShadowLayer(mStrokeWidth, 0, mStrokeWidth, Color.argb(SHADOW_OPACITY, 0, 0, 0));
+            canvas.drawCircle(radius, radius, radius, paint);
+
+            canvas.restore();
+        }
+
+        public void drawBackgroundStroke(Canvas canvas, Paint paint) {
+            canvas.save();
+            canvas.translate(getOffsetX(), getOffsetY());
+
+            paint.reset();
+            paint.setAntiAlias(true);
+            paint.setColor(Color.argb(255, BG_INTENSITY, BG_INTENSITY, BG_INTENSITY));
+            paint.setStyle(Paint.Style.STROKE);
+            paint.setStrokeWidth(mStrokeWidth);
+
+            float radius = getScaledRadius();
+            canvas.drawCircle(radius, radius, radius - 1, paint);
+
+            canvas.restore();
+        }
+
+        public void drawLeaveBehind(Canvas canvas, Paint paint) {
+            float originalScale = mScale;
+            mScale = 0.5f;
+
+            canvas.save();
+            canvas.translate(getOffsetX(), getOffsetY());
+
+            paint.reset();
+            paint.setAntiAlias(true);
+            paint.setColor(Color.argb(160, 245, 245, 245));
+
+            float radius = getScaledRadius();
+            canvas.drawCircle(radius, radius, radius, paint);
+
+            canvas.restore();
+            mScale = originalScale;
+        }
+
+        // It is the callers responsibility to save and restore the canvas.
+        private void clipCanvas(Canvas canvas) {
+            canvas.translate(getOffsetX(), getOffsetY());
+            canvas.clipPath(mClipPath);
+            canvas.translate(-getOffsetX(), -getOffsetY());
+        }
+
+        private void delegateDrawing(CellLayout delegate, int cellX, int cellY) {
+            if (mDrawingDelegate != delegate) {
+                delegate.addFolderBackground(this);
+            }
+
+            mDrawingDelegate = delegate;
+            delegateCellX = cellX;
+            delegateCellY = cellY;
+
+            invalidate();
+        }
+
+        private void clearDrawingDelegate() {
+            if (mDrawingDelegate != null) {
+                mDrawingDelegate.removeFolderBackground(this);
+            }
+
+            mDrawingDelegate = null;
+            invalidate();
+        }
+
+        private boolean drawingDelegated() {
+            return mDrawingDelegate != null;
+        }
+
+        private void animateScale(float finalScale, float finalMultiplier,
+                final Runnable onStart, final Runnable onEnd) {
+            final float scale0 = mScale;
+            final float scale1 = finalScale;
+
+            final float bgMultiplier0 = mColorMultiplier;
+            final float bgMultiplier1 = finalMultiplier;
+
+            if (mScaleAnimator != null) {
+                mScaleAnimator.cancel();
+            }
+
+            mScaleAnimator = LauncherAnimUtils.ofFloat(null, 0f, 1.0f);
+
+            mScaleAnimator.addUpdateListener(new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    float prog = animation.getAnimatedFraction();
+                    mScale = prog * scale1 + (1 - prog) * scale0;
+                    mColorMultiplier = prog * bgMultiplier1 + (1 - prog) * bgMultiplier0;
+                    invalidate();
+                }
+            });
+            mScaleAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    if (onStart != null) {
+                        onStart.run();
+                    }
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (onEnd != null) {
+                        onEnd.run();
+                    }
+                    mScaleAnimator = null;
+                }
+            });
+
+            mScaleAnimator.setDuration(CONSUMPTION_ANIMATION_DURATION);
+            mScaleAnimator.start();
+        }
+
+        public void animateToAccept(final CellLayout cl, final int cellX, final int cellY) {
+            Runnable onStart = new Runnable() {
+                @Override
+                public void run() {
+                    delegateDrawing(cl, cellX, cellY);
+                }
+            };
+            animateScale(ACCEPT_SCALE_FACTOR, ACCEPT_COLOR_MULTIPLIER, onStart, null);
+        }
+
+        public void animateToRest() {
+            // This can be called multiple times -- we need to make sure the drawing delegate
+            // is saved and restored at the beginning of the animation, since cancelling the
+            // existing animation can clear the delgate.
+            final CellLayout cl = mDrawingDelegate;
+            final int cellX = delegateCellX;
+            final int cellY = delegateCellY;
+
+            Runnable onStart = new Runnable() {
+                @Override
+                public void run() {
+                    delegateDrawing(cl, cellX, cellY);
+                }
+            };
+            Runnable onEnd = new Runnable() {
+                @Override
+                public void run() {
+                    clearDrawingDelegate();
+                }
+            };
+            animateScale(1f, 1f, onStart, onEnd);
+        }
+    }
+
+    public void setFolderBackground(PreviewBackground bg) {
+        mBackground = bg;
+        mBackground.setInvalidateDelegate(this);
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        super.dispatchDraw(canvas);
+
+        if (mReferenceDrawable != null) {
+            computePreviewDrawingParams(mReferenceDrawable);
+        }
+
+        if (!mBackground.drawingDelegated()) {
+            mBackground.drawBackground(canvas, mBgPaint);
+        }
+
+        if (mFolder == null) return;
+        if (mFolder.getItemCount() == 0 && !mAnimating) return;
+
+        canvas.save();
+
+
+        if (mPreviewLayoutRule.clipToBackground()) {
+            mBackground.clipCanvas(canvas);
+        }
+
+        // The items are drawn in coordinates relative to the preview offset
+        canvas.translate(mBackground.basePreviewOffsetX, mBackground.basePreviewOffsetY);
+
+        // The first item should be drawn last (ie. on top of later items)
+        for (int i = mDrawingParams.size() - 1; i >= 0; i--) {
+            PreviewItemDrawingParams p = mDrawingParams.get(i);
+            if (!p.hidden) {
+                drawPreviewItem(canvas, p);
+            }
+        }
+        canvas.restore();
+
+        if (mPreviewLayoutRule.clipToBackground() && !mBackground.drawingDelegated()) {
+            mBackground.drawBackgroundStroke(canvas, mBgPaint);
+        }
+    }
+
+    private Drawable getTopDrawable(TextView v) {
+        Drawable d = v.getCompoundDrawables()[1];
+        return (d instanceof PreloadIconDrawable) ? ((PreloadIconDrawable) d).mIcon : d;
+    }
+
+    class FolderPreviewItemAnim {
+        ValueAnimator mValueAnimator;
+        float finalScale;
+        float finalTransX;
+        float finalTransY;
+
+        /**
+         *
+         * @param params layout params to animate
+         * @param index0 original index of the item to be animated
+         * @param nItems0 original number of items in the preview
+         * @param index1 new index of the item to be animated
+         * @param nItems1 new number of items in the preview
+         * @param duration duration in ms of the animation
+         * @param onCompleteRunnable runnable to execute upon animation completion
+         */
+        public FolderPreviewItemAnim(final PreviewItemDrawingParams params, int index0, int nItems0,
+                int index1, int nItems1, int duration, final Runnable onCompleteRunnable) {
+
+            computePreviewItemDrawingParams(index1, nItems1, mTmpParams);
+
+            finalScale = mTmpParams.scale;
+            finalTransX = mTmpParams.transX;
+            finalTransY = mTmpParams.transY;
+
+            computePreviewItemDrawingParams(index0, nItems0, mTmpParams);
+
+            final float scale0 = mTmpParams.scale;
+            final float transX0 = mTmpParams.transX;
+            final float transY0 = mTmpParams.transY;
+
+            mValueAnimator = LauncherAnimUtils.ofFloat(FolderIcon.this, 0f, 1.0f);
+            mValueAnimator.addUpdateListener(new AnimatorUpdateListener(){
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    float progress = animation.getAnimatedFraction();
+
+                    params.transX = transX0 + progress * (finalTransX - transX0);
+                    params.transY = transY0 + progress * (finalTransY - transY0);
+                    params.scale = scale0 + progress * (finalScale - scale0);
+                    invalidate();
+                }
+            });
+
+            mValueAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                }
+
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (onCompleteRunnable != null) {
+                        onCompleteRunnable.run();
+                    }
+                    params.anim = null;
+                }
+            });
+            mValueAnimator.setDuration(duration);
+        }
+
+        public void start() {
+            mValueAnimator.start();
+        }
+
+        public void cancel() {
+            mValueAnimator.cancel();
+        }
+
+        public boolean hasEqualFinalState(FolderPreviewItemAnim anim) {
+            return finalTransY == anim.finalTransY && finalTransX == anim.finalTransX &&
+                    finalScale == anim.finalScale;
+
+        }
+    }
+
+    private void animateFirstItem(final Drawable d, int duration, final boolean reverse,
+            final Runnable onCompleteRunnable) {
+
+        FolderPreviewItemAnim anim;
+        if (!reverse) {
+            anim = new FolderPreviewItemAnim(mDrawingParams.get(0), -1, -1, 0, 2, duration,
+                    onCompleteRunnable);
+        } else {
+            anim = new FolderPreviewItemAnim(mDrawingParams.get(0), 0, 2, -1, -1, duration,
+                    onCompleteRunnable);
+        }
+        anim.start();
+    }
+
+    public void setTextVisible(boolean visible) {
+        if (visible) {
+            mFolderName.setVisibility(VISIBLE);
+        } else {
+            mFolderName.setVisibility(INVISIBLE);
+        }
+    }
+
+    public boolean getTextVisible() {
+        return mFolderName.getVisibility() == VISIBLE;
+    }
+
+    private void updateItemDrawingParams(boolean animate) {
+        ArrayList<View> items = mFolder.getItemsInReadingOrder();
+        int nItemsInPreview = Math.min(items.size(), mPreviewLayoutRule.numItems());
+
+        int prevNumItems = mDrawingParams.size();
+
+        // We adjust the size of the list to match the number of items in the preview
+        while (nItemsInPreview < mDrawingParams.size()) {
+            mDrawingParams.remove(mDrawingParams.size() - 1);
+        }
+        while (nItemsInPreview > mDrawingParams.size()) {
+            mDrawingParams.add(new PreviewItemDrawingParams(0, 0, 0, 0));
+        }
+
+        for (int i = 0; i < mDrawingParams.size(); i++) {
+            PreviewItemDrawingParams p = mDrawingParams.get(i);
+            p.drawable = getTopDrawable((TextView) items.get(i));
+
+            if (!animate || FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON) {
+                computePreviewItemDrawingParams(i, nItemsInPreview, p);
+                if (mReferenceDrawable == null) {
+                    mReferenceDrawable = p.drawable;
+                }
+            } else {
+                FolderPreviewItemAnim anim = new FolderPreviewItemAnim(p, i, prevNumItems, i,
+                        nItemsInPreview, DROP_IN_ANIMATION_DURATION, null);
+
+                if (p.anim != null) {
+                    if (p.anim.hasEqualFinalState(anim)) {
+                        // do nothing, let the current animation finish
+                        continue;
+                    }
+                    p.anim.cancel();
+                }
+                p.anim = anim;
+                p.anim.start();
+            }
+        }
+    }
+
+    @Override
+    public void onItemsChanged(boolean animate) {
+        updateItemDrawingParams(animate);
+        invalidate();
+        requestLayout();
+    }
+
+    public void onAdd(ShortcutInfo item) {
+        invalidate();
+        requestLayout();
+    }
+
+    public void onRemove(ShortcutInfo item) {
+        invalidate();
+        requestLayout();
+    }
+
+    public void onTitleChanged(CharSequence title) {
+        mFolderName.setText(title);
+        setContentDescription(getContext().getString(R.string.folder_name_format, title));
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        // Call the superclass onTouchEvent first, because sometimes it changes the state to
+        // isPressed() on an ACTION_UP
+        boolean result = super.onTouchEvent(event);
+
+        // Check for a stylus button press, if it occurs cancel any long press checks.
+        if (mStylusEventHelper.onMotionEvent(event)) {
+            mLongPressHelper.cancelLongPress();
+            return true;
+        }
+
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mLongPressHelper.postCheckForLongPress();
+                break;
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                mLongPressHelper.cancelLongPress();
+                break;
+            case MotionEvent.ACTION_MOVE:
+                if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
+                    mLongPressHelper.cancelLongPress();
+                }
+                break;
+        }
+        return result;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+    }
+
+    @Override
+    public void cancelLongPress() {
+        super.cancelLongPress();
+        mLongPressHelper.cancelLongPress();
+    }
+
+    public void removeListeners() {
+        mInfo.removeListener(this);
+        mInfo.removeListener(mFolder);
+    }
+
+    public interface PreviewLayoutRule {
+        public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
+            PreviewItemDrawingParams params);
+
+        public void init(int availableSpace, int intrinsicIconSize, boolean rtl);
+
+        public int numItems();
+        public boolean clipToBackground();
+    }
+}
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
similarity index 90%
rename from src/com/android/launcher3/FolderPagedView.java
rename to src/com/android/launcher3/folder/FolderPagedView.java
index d503d2c..7e7ee34 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -14,22 +14,38 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.folder;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
+import android.graphics.Canvas;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewDebug;
 import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-import android.view.animation.OvershootInterpolator;
 
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener;
-import com.android.launcher3.PageIndicator.PageMarkerResources;
+import com.android.launcher3.IconCache;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.keyboard.ViewGroupFocusHelper;
+import com.android.launcher3.pageindicators.PageIndicator;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -47,13 +63,6 @@
     private static final int START_VIEW_REORDER_DELAY = 30;
     private static final float VIEW_REORDER_DELAY_FACTOR = 0.9f;
 
-    private static final int PAGE_INDICATOR_ANIMATION_START_DELAY = 300;
-    private static final int PAGE_INDICATOR_ANIMATION_STAGGERED_DELAY = 150;
-    private static final int PAGE_INDICATOR_ANIMATION_DURATION = 400;
-
-    // This value approximately overshoots to 1.5 times the original size.
-    private static final float PAGE_INDICATOR_OVERSHOOT_TENSION = 4.9f;
-
     /**
      * Fraction of the width to scroll when showing the next page hint.
      */
@@ -65,19 +74,24 @@
 
     private final LayoutInflater mInflater;
     private final IconCache mIconCache;
+    private final ViewGroupFocusHelper mFocusIndicatorHelper;
 
     @Thunk final HashMap<View, Runnable> mPendingAnimations = new HashMap<>();
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     private final int mMaxCountX;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private final int mMaxCountY;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private final int mMaxItemsPerPage;
 
     private int mAllocatedContentSize;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private int mGridCountX;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private int mGridCountY;
 
     private Folder mFolder;
-    private FocusIndicatorView mFocusIndicatorView;
     private PagedFolderKeyEventListener mKeyListener;
 
     private PageIndicator mPageIndicator;
@@ -99,13 +113,14 @@
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
 
         setEdgeGlowColor(getResources().getColor(R.color.folder_edge_effect_color));
+        mFocusIndicatorHelper = new ViewGroupFocusHelper(this);
     }
 
     public void setFolder(Folder folder) {
         mFolder = folder;
-        mFocusIndicatorView = (FocusIndicatorView) folder.findViewById(R.id.focus_indicator);
         mKeyListener = new PagedFolderKeyEventListener(folder);
         mPageIndicator = (PageIndicator) folder.findViewById(R.id.folder_page_indicator);
+        initParentViews(folder);
     }
 
     /**
@@ -149,6 +164,12 @@
         }
     }
 
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        mFocusIndicatorHelper.draw(canvas);
+        super.dispatchDraw(canvas);
+    }
+
     /**
      * Binds items to the layout.
      * @return list of items that could not be bound, probably because we hit the max size limit.
@@ -172,9 +193,9 @@
      * Create space for a new item at the end, and returns the rank for that item.
      * Also sets the current page to the last page.
      */
-    public int allocateRankForNewItem(ShortcutInfo info) {
+    public int allocateRankForNewItem() {
         int rank = getItemCount();
-        ArrayList<View> views = new ArrayList<View>(mFolder.getItemsInReadingOrder());
+        ArrayList<View> views = new ArrayList<>(mFolder.getItemsInReadingOrder());
         views.add(rank, null);
         arrangeChildren(views, views.size(), false);
         setCurrentPage(rank / mMaxItemsPerPage);
@@ -213,7 +234,7 @@
         textView.applyFromShortcutInfo(item, mIconCache);
         textView.setOnClickListener(mFolder);
         textView.setOnLongClickListener(mFolder);
-        textView.setOnFocusChangeListener(mFocusIndicatorView);
+        textView.setOnFocusChangeListener(mFocusIndicatorHelper);
         textView.setOnKeyListener(mKeyListener);
 
         textView.setLayoutParams(new CellLayout.LayoutParams(
@@ -226,18 +247,12 @@
         return (CellLayout) getChildAt(index);
     }
 
-    public void removeCellLayoutView(View view) {
-        for (int i = getChildCount() - 1; i >= 0; i --) {
-            getPageAt(i).removeView(view);
-        }
-    }
-
     public CellLayout getCurrentCellLayout() {
         return getPageAt(getNextPage());
     }
 
     private CellLayout createAndAddNewPage() {
-        DeviceProfile grid = ((Launcher) getContext()).getDeviceProfile();
+        DeviceProfile grid = Launcher.getLauncher(getContext()).getDeviceProfile();
         CellLayout page = new CellLayout(getContext());
         page.setCellDimensions(grid.folderCellWidthPx, grid.folderCellHeightPx);
         page.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false);
@@ -268,6 +283,12 @@
         }
     }
 
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        mPageIndicator.setScroll(l, mMaxScrollX);
+    }
+
     /**
      * Updates position and rank of all the children in the view.
      * It essentially removes all views from all the pages and then adds them again in appropriate
@@ -392,12 +413,6 @@
                 pageIndex * mMaxItemsPerPage + sTempPosArray[1] * mGridCountX + sTempPosArray[0]);
     }
 
-    @Override
-    protected PageMarkerResources getPageIndicatorMarker(int pageIndex) {
-        return new PageMarkerResources(R.drawable.ic_pageindicator_current_folder,
-                R.drawable.ic_pageindicator_default_folder);
-    }
-
     public boolean isFull() {
         return !ALLOW_FOLDER_SCROLL && getItemCount() >= mMaxItemsPerPage;
     }
@@ -437,7 +452,7 @@
             for (int j = 0; j < page.getCountY(); j++) {
                 for (int i = 0; i < page.getCountX(); i++) {
                     View v = page.getChildAt(i, j);
-                    if ((v != null) && op.evaluate((ItemInfo) v.getTag(), v, this)) {
+                    if ((v != null) && op.evaluate((ItemInfo) v.getTag(), v)) {
                         return v;
                     }
                 }
@@ -447,8 +462,7 @@
     }
 
     public String getAccessibilityDescription() {
-        return String.format(getContext().getString(R.string.folder_opened),
-                mGridCountX, mGridCountY);
+        return getContext().getString(R.string.folder_opened, mGridCountX, mGridCountY);
     }
 
     /**
@@ -660,28 +674,6 @@
         }
     }
 
-    public void setMarkerScale(float scale) {
-        int count  = mPageIndicator.getChildCount();
-        for (int i = 0; i < count; i++) {
-            View marker = mPageIndicator.getChildAt(i);
-            marker.animate().cancel();
-            marker.setScaleX(scale);
-            marker.setScaleY(scale);
-        }
-    }
-
-    public void animateMarkers() {
-        int count  = mPageIndicator.getChildCount();
-        Interpolator interpolator = new OvershootInterpolator(PAGE_INDICATOR_OVERSHOOT_TENSION);
-        for (int i = 0; i < count; i++) {
-            mPageIndicator.getChildAt(i).animate().scaleX(1).scaleY(1)
-                .setInterpolator(interpolator)
-                .setDuration(PAGE_INDICATOR_ANIMATION_DURATION)
-                .setStartDelay(PAGE_INDICATOR_ANIMATION_STAGGERED_DELAY * i
-                        + PAGE_INDICATOR_ANIMATION_START_DELAY);
-        }
-    }
-
     public int itemsPerPage() {
         return mMaxItemsPerPage;
     }
diff --git a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
new file mode 100644
index 0000000..7fb02e3
--- /dev/null
+++ b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
@@ -0,0 +1,91 @@
+/**
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.folder;
+
+import android.graphics.Path;
+
+import com.android.launcher3.folder.FolderIcon.PreviewItemDrawingParams;
+
+public class StackFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule {
+
+    static final int MAX_NUM_ITEMS_IN_PREVIEW = 3;
+
+    // The degree to which the item in the back of the stack is scaled [0...1]
+    // (0 means it's not scaled at all, 1 means it's scaled to nothing)
+    private static final float PERSPECTIVE_SCALE_FACTOR = 0.35f;
+
+    // The amount of vertical spread between items in the stack [0...1]
+    private static final float PERSPECTIVE_SHIFT_FACTOR = 0.18f;
+
+    private float mBaselineIconScale;
+    private int mBaselineIconSize;
+    private int mAvailableSpaceInPreview;
+    private float mMaxPerspectiveShift;
+
+    @Override
+    public void init(int availableSpace, int intrinsicIconSize, boolean rtl) {
+        mAvailableSpaceInPreview = availableSpace;
+
+        // cos(45) = 0.707  + ~= 0.1) = 0.8f
+        int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f));
+
+        int unscaledHeight = (int) (intrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR));
+
+        mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight);
+
+        mBaselineIconSize = (int) (intrinsicIconSize * mBaselineIconScale);
+        mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR;
+    }
+
+    @Override
+    public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
+            PreviewItemDrawingParams params) {
+
+        index = MAX_NUM_ITEMS_IN_PREVIEW - index - 1;
+        float r = (index * 1.0f) / (MAX_NUM_ITEMS_IN_PREVIEW - 1);
+        float scale = (1 - PERSPECTIVE_SCALE_FACTOR * (1 - r));
+
+        float offset = (1 - r) * mMaxPerspectiveShift;
+        float scaledSize = scale * mBaselineIconSize;
+        float scaleOffsetCorrection = (1 - scale) * mBaselineIconSize;
+
+        // We want to imagine our coordinates from the bottom left, growing up and to the
+        // right. This is natural for the x-axis, but for the y-axis, we have to invert things.
+        float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection);
+        float transX = (mAvailableSpaceInPreview - scaledSize) / 2;
+        float totalScale = mBaselineIconScale * scale;
+        final float overlayAlpha = (80 * (1 - r)) / 255f;
+
+        if (params == null) {
+            params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
+        } else {
+            params.update(transX, transY, totalScale);
+            params.overlayAlpha = overlayAlpha;
+        }
+        return params;
+    }
+
+    @Override
+    public int numItems() {
+        return MAX_NUM_ITEMS_IN_PREVIEW;
+    }
+
+    @Override
+    public boolean clipToBackground() {
+        return false;
+    }
+}
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
new file mode 100644
index 0000000..bc91c15
--- /dev/null
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Region.Op;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.launcher3.HolographicOutlineHelper;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.PreloadIconDrawable;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.folder.FolderIcon;
+
+/**
+ * A utility class to generate preview bitmap for dragging.
+ */
+public class DragPreviewProvider {
+
+    public static final int DRAG_BITMAP_PADDING = 2;
+
+    private final Rect mTempRect = new Rect();
+
+    protected final View mView;
+
+    // The padding added to the drag view during the preview generation.
+    public final int previewPadding;
+
+    public Bitmap gerenatedDragOutline;
+
+    public DragPreviewProvider(View view) {
+        mView = view;
+
+        if (mView instanceof TextView) {
+            Drawable d = Workspace.getTextViewIcon((TextView) mView);
+            Rect bounds = getDrawableBounds(d);
+            previewPadding = DRAG_BITMAP_PADDING - bounds.left - bounds.top;
+        } else {
+            previewPadding = DRAG_BITMAP_PADDING;
+        }
+    }
+
+    /**
+     * Draws the {@link #mView} into the given {@param destCanvas}.
+     */
+    private void drawDragView(Canvas destCanvas) {
+        destCanvas.save();
+        if (mView instanceof TextView) {
+            Drawable d = Workspace.getTextViewIcon((TextView) mView);
+            Rect bounds = getDrawableBounds(d);
+            destCanvas.translate(DRAG_BITMAP_PADDING / 2 - bounds.left,
+                    DRAG_BITMAP_PADDING / 2 - bounds.top);
+            d.draw(destCanvas);
+        } else {
+            final Rect clipRect = mTempRect;
+            mView.getDrawingRect(clipRect);
+
+            boolean textVisible = false;
+            if (mView instanceof FolderIcon) {
+                // For FolderIcons the text can bleed into the icon area, and so we need to
+                // hide the text completely (which can't be achieved by clipping).
+                if (((FolderIcon) mView).getTextVisible()) {
+                    ((FolderIcon) mView).setTextVisible(false);
+                    textVisible = true;
+                }
+            }
+            destCanvas.translate(-mView.getScrollX() + DRAG_BITMAP_PADDING / 2,
+                    -mView.getScrollY() + DRAG_BITMAP_PADDING / 2);
+            destCanvas.clipRect(clipRect, Op.REPLACE);
+            mView.draw(destCanvas);
+
+            // Restore text visibility of FolderIcon if necessary
+            if (textVisible) {
+                ((FolderIcon) mView).setTextVisible(true);
+            }
+        }
+        destCanvas.restore();
+    }
+
+    /**
+     * Returns a new bitmap to show when the {@link #mView} is being dragged around.
+     * Responsibility for the bitmap is transferred to the caller.
+     */
+    public Bitmap createDragBitmap(Canvas canvas) {
+        Bitmap b;
+
+        if (mView instanceof TextView) {
+            Drawable d = Workspace.getTextViewIcon((TextView) mView);
+            Rect bounds = getDrawableBounds(d);
+            b = Bitmap.createBitmap(bounds.width() + DRAG_BITMAP_PADDING,
+                    bounds.height() + DRAG_BITMAP_PADDING, Bitmap.Config.ARGB_8888);
+        } else {
+            b = Bitmap.createBitmap(mView.getWidth() + DRAG_BITMAP_PADDING,
+                    mView.getHeight() + DRAG_BITMAP_PADDING, Bitmap.Config.ARGB_8888);
+        }
+
+        canvas.setBitmap(b);
+        drawDragView(canvas);
+        canvas.setBitmap(null);
+
+        return b;
+    }
+
+    public final void generateDragOutline(Canvas canvas) {
+        if (ProviderConfig.IS_DOGFOOD_BUILD && gerenatedDragOutline != null) {
+            throw new RuntimeException("Drag outline generated twice");
+        }
+
+        gerenatedDragOutline = createDragOutline(canvas);
+    }
+
+    /**
+     * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
+     * Responsibility for the bitmap is transferred to the caller.
+     */
+    public Bitmap createDragOutline(Canvas canvas) {
+        final Bitmap b = Bitmap.createBitmap(mView.getWidth() + DRAG_BITMAP_PADDING,
+                mView.getHeight() + DRAG_BITMAP_PADDING, Bitmap.Config.ALPHA_8);
+        canvas.setBitmap(b);
+        drawDragView(canvas);
+        HolographicOutlineHelper.obtain(mView.getContext())
+                .applyExpensiveOutlineWithBlur(b, canvas);
+        canvas.setBitmap(null);
+        return b;
+    }
+
+    protected static Rect getDrawableBounds(Drawable d) {
+        Rect bounds = new Rect();
+        d.copyBounds(bounds);
+        if (bounds.width() == 0 || bounds.height() == 0) {
+            bounds.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
+        } else {
+            bounds.offsetTo(0, 0);
+        }
+        if (d instanceof PreloadIconDrawable) {
+            int inset = -((PreloadIconDrawable) d).getOutset();
+            bounds.inset(inset, inset);
+        }
+        return bounds;
+    }
+
+    public float getScaleAndPosition(Bitmap preview, int[] outPos) {
+        float scale = Launcher.getLauncher(mView.getContext())
+                .getDragLayer().getLocationInDragLayer(mView, outPos);
+        outPos[0] = Math.round(outPos[0] - (preview.getWidth() - scale * mView.getWidth()) / 2);
+        outPos[1] = Math.round(outPos[1] - (1 - scale) * preview.getHeight() / 2 - previewPadding / 2);
+        return scale;
+    }
+}
diff --git a/src/com/android/launcher3/graphics/ShadowGenerator.java b/src/com/android/launcher3/graphics/ShadowGenerator.java
new file mode 100644
index 0000000..2b24ec9
--- /dev/null
+++ b/src/com/android/launcher3/graphics/ShadowGenerator.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.graphics;
+
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.BlurMaskFilter;
+import android.graphics.BlurMaskFilter.Blur;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.util.Preconditions;
+
+/**
+ * Utility class to add shadows to bitmaps.
+ */
+public class ShadowGenerator {
+
+    // Percent of actual icon size
+    private static final float HALF_DISTANCE = 0.5f;
+    private static final float BLUR_FACTOR = 0.5f/48;
+
+    // Percent of actual icon size
+    private static final float KEY_SHADOW_DISTANCE = 1f/48;
+    private static final int KEY_SHADOW_ALPHA = 61;
+
+    private static final int AMBIENT_SHADOW_ALPHA = 30;
+
+    private static final Object LOCK = new Object();
+    // Singleton object guarded by {@link #LOCK}
+    private static ShadowGenerator sShadowGenerator;
+
+    private final int mIconSize;
+
+    private final Canvas mCanvas;
+    private final Paint mBlurPaint;
+    private final Paint mDrawPaint;
+
+    private ShadowGenerator() {
+        mIconSize = LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize;
+        mCanvas = new Canvas();
+        mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+        mBlurPaint.setMaskFilter(new BlurMaskFilter(mIconSize * BLUR_FACTOR, Blur.NORMAL));
+        mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+    }
+
+    public synchronized Bitmap recreateIcon(Bitmap icon) {
+        int[] offset = new int[2];
+        Bitmap shadow = icon.extractAlpha(mBlurPaint, offset);
+        Bitmap result = Bitmap.createBitmap(mIconSize, mIconSize, Config.ARGB_8888);
+        mCanvas.setBitmap(result);
+
+        // Draw ambient shadow
+        mDrawPaint.setAlpha(AMBIENT_SHADOW_ALPHA);
+        mCanvas.drawBitmap(shadow, offset[0], offset[1], mDrawPaint);
+
+        // Draw key shadow
+        mDrawPaint.setAlpha(KEY_SHADOW_ALPHA);
+        mCanvas.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconSize, mDrawPaint);
+
+        // Draw the icon
+        mDrawPaint.setAlpha(255);
+        mCanvas.drawBitmap(icon, 0, 0, mDrawPaint);
+
+        mCanvas.setBitmap(null);
+        return result;
+    }
+
+    public static ShadowGenerator getInstance() {
+        Preconditions.assertNonUiThread();
+        synchronized (LOCK) {
+            if (sShadowGenerator == null) {
+                sShadowGenerator = new ShadowGenerator();
+            }
+        }
+        return sShadowGenerator;
+    }
+
+    /**
+     * Returns the minimum amount by which an icon with {@param bounds} should be scaled
+     * so that the shadows do not get clipped.
+     */
+    public static float getScaleForBounds(RectF bounds) {
+        float scale = 1;
+
+        // For top, left & right, we need same space.
+        float minSide = Math.min(Math.min(bounds.left, bounds.right), bounds.top);
+        if (minSide < BLUR_FACTOR) {
+            scale = (HALF_DISTANCE - BLUR_FACTOR) / (HALF_DISTANCE - minSide);
+        }
+
+        float bottomSpace = BLUR_FACTOR + KEY_SHADOW_DISTANCE;
+        if (bounds.bottom < bottomSpace) {
+            scale = Math.min(scale, (HALF_DISTANCE - bottomSpace) / (HALF_DISTANCE - bounds.bottom));
+        }
+        return scale;
+    }
+}
diff --git a/src/com/android/launcher3/graphics/TintedDrawableSpan.java b/src/com/android/launcher3/graphics/TintedDrawableSpan.java
new file mode 100644
index 0000000..f72ce03
--- /dev/null
+++ b/src/com/android/launcher3/graphics/TintedDrawableSpan.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.graphics;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.FontMetricsInt;
+import android.graphics.drawable.Drawable;
+import android.text.style.DynamicDrawableSpan;
+
+/**
+ * {@link DynamicDrawableSpan} which draws a drawable tinted with the current paint color.
+ */
+public class TintedDrawableSpan extends DynamicDrawableSpan {
+
+    private final Drawable mDrawable;
+    private int mOldTint;
+
+    public TintedDrawableSpan(Context context, int resourceId) {
+        super(ALIGN_BOTTOM);
+        mDrawable = context.getDrawable(resourceId);
+        mOldTint = 0;
+    }
+
+    @Override
+    public int getSize(Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) {
+        fm = fm == null ? paint.getFontMetricsInt() : fm;
+        int iconSize = fm.bottom - fm.top;
+        mDrawable.setBounds(0, 0, iconSize, iconSize);
+        return super.getSize(paint, text, start, end, fm);
+    }
+
+    @Override
+    public void draw(Canvas canvas, CharSequence text,
+            int start, int end, float x, int top, int y, int bottom, Paint paint) {
+        int color = paint.getColor();
+        if (mOldTint != color) {
+            mOldTint = color;
+            mDrawable.setTint(mOldTint);
+        }
+        super.draw(canvas, text, start, end, x, top, y, bottom, paint);
+    }
+
+    @Override
+    public Drawable getDrawable() {
+        return mDrawable;
+    }
+}
diff --git a/src/com/android/launcher3/graphics/TriangleShape.java b/src/com/android/launcher3/graphics/TriangleShape.java
new file mode 100644
index 0000000..cce4e3c
--- /dev/null
+++ b/src/com/android/launcher3/graphics/TriangleShape.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.graphics;
+
+import android.graphics.Outline;
+import android.graphics.Path;
+import android.graphics.drawable.shapes.PathShape;
+import android.support.annotation.NonNull;
+
+/**
+ * Wrapper around {@link android.graphics.drawable.shapes.PathShape}
+ * that creates a shape with a triangular path (pointing up or down).
+ */
+public class TriangleShape extends PathShape {
+    private Path mTriangularPath;
+
+    public TriangleShape(Path path, float stdWidth, float stdHeight) {
+        super(path, stdWidth, stdHeight);
+        mTriangularPath = path;
+    }
+
+    public static TriangleShape create(float width, float height, boolean isPointingUp) {
+        Path triangularPath = new Path();
+        if (isPointingUp) {
+            triangularPath.moveTo(0, height);
+            triangularPath.lineTo(width, height);
+            triangularPath.lineTo(width / 2, 0);
+            triangularPath.close();
+        } else {
+            triangularPath.moveTo(0, 0);
+            triangularPath.lineTo(width / 2, height);
+            triangularPath.lineTo(width, 0);
+            triangularPath.close();
+        }
+        return new TriangleShape(triangularPath, width, height);
+    }
+
+    @Override
+    public void getOutline(@NonNull Outline outline) {
+        outline.setConvexPath(mTriangularPath);
+    }
+}
diff --git a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
new file mode 100644
index 0000000..7672f5a
--- /dev/null
+++ b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.keyboard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.RectEvaluator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.TargetApi;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.os.Build.VERSION_CODES;
+import android.util.Property;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+
+import com.android.launcher3.R;
+
+/**
+ * A helper class to draw background of a focused view.
+ */
+@TargetApi(VERSION_CODES.LOLLIPOP)
+public abstract class FocusIndicatorHelper implements
+        OnFocusChangeListener, AnimatorUpdateListener {
+
+    private static final float MIN_VISIBLE_ALPHA = 0.2f;
+    private static final long ANIM_DURATION = 150;
+
+    public static final Property<FocusIndicatorHelper, Float> ALPHA =
+            new Property<FocusIndicatorHelper, Float>(Float.TYPE, "alpha") {
+                @Override
+                public void set(FocusIndicatorHelper object, Float value) {
+                    object.setAlpha(value);
+                }
+
+                @Override
+                public Float get(FocusIndicatorHelper object) {
+                    return object.mAlpha;
+                }
+            };
+
+    public static final Property<FocusIndicatorHelper, Float> SHIFT =
+            new Property<FocusIndicatorHelper, Float>(
+                    Float.TYPE, "shift") {
+
+                @Override
+                public void set(FocusIndicatorHelper object, Float value) {
+                    object.mShift = value;
+                }
+
+                @Override
+                public Float get(FocusIndicatorHelper object) {
+                    return object.mShift;
+                }
+            };
+
+    private static final RectEvaluator RECT_EVALUATOR = new RectEvaluator(new Rect());
+    private static final Rect sTempRect1 = new Rect();
+    private static final Rect sTempRect2 = new Rect();
+
+    private final View mContainer;
+    private final Paint mPaint;
+    private final int mMaxAlpha;
+
+    private final Rect mDirtyRect = new Rect();
+    private boolean mIsDirty = false;
+
+    private View mLastFocusedView;
+
+    private View mCurrentView;
+    private View mTargetView;
+    /**
+     * The fraction indicating the position of the focusRect between {@link #mCurrentView}
+     * & {@link #mTargetView}
+     */
+    private float mShift;
+
+    private ObjectAnimator mCurrentAnimation;
+    private float mAlpha;
+
+    public FocusIndicatorHelper(View container) {
+        mContainer = container;
+
+        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        int color = container.getResources().getColor(R.color.focused_background);
+        mMaxAlpha = Color.alpha(color);
+        mPaint.setColor(0xFF000000 | color);
+
+        setAlpha(0);
+        mShift = 0;
+    }
+
+    protected void setAlpha(float alpha) {
+        mAlpha = alpha;
+        mPaint.setAlpha((int) (mAlpha * mMaxAlpha));
+    }
+
+    @Override
+    public void onAnimationUpdate(ValueAnimator animation) {
+        invalidateDirty();
+    }
+
+    protected void invalidateDirty() {
+        if (mIsDirty) {
+            mContainer.invalidate(mDirtyRect);
+            mIsDirty = false;
+        }
+
+        Rect newRect = getDrawRect();
+        if (newRect != null) {
+            mContainer.invalidate(newRect);
+        }
+    }
+
+    public void draw(Canvas c) {
+        if (mAlpha > 0) {
+            Rect newRect = getDrawRect();
+            if (newRect != null) {
+                mDirtyRect.set(newRect);
+                c.drawRect(mDirtyRect, mPaint);
+                mIsDirty = true;
+            }
+        }
+    }
+
+    private Rect getDrawRect() {
+        if (mCurrentView != null) {
+            viewToRect(mCurrentView, sTempRect1);
+
+            if (mShift > 0 && mTargetView != null) {
+                viewToRect(mTargetView, sTempRect2);
+                return RECT_EVALUATOR.evaluate(mShift, sTempRect1, sTempRect2);
+            } else {
+                return sTempRect1;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void onFocusChange(View v, boolean hasFocus) {
+        if (hasFocus) {
+            endCurrentAnimation();
+
+            if (mAlpha > MIN_VISIBLE_ALPHA) {
+                mTargetView = v;
+
+                mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+                        PropertyValuesHolder.ofFloat(ALPHA, 1),
+                        PropertyValuesHolder.ofFloat(SHIFT, 1));
+                mCurrentAnimation.addListener(new ViewSetListener(v, true));
+            } else {
+                setCurrentView(v);
+
+                mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+                        PropertyValuesHolder.ofFloat(ALPHA, 1));
+            }
+
+            mLastFocusedView = v;
+        } else {
+            if (mLastFocusedView == v) {
+                mLastFocusedView = null;
+                endCurrentAnimation();
+                mCurrentAnimation = ObjectAnimator.ofPropertyValuesHolder(this,
+                        PropertyValuesHolder.ofFloat(ALPHA, 0));
+                mCurrentAnimation.addListener(new ViewSetListener(null, false));
+            }
+        }
+
+        // invalidate once
+        invalidateDirty();
+
+        mLastFocusedView = hasFocus ? v : null;
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.addUpdateListener(this);
+            mCurrentAnimation.setDuration(ANIM_DURATION).start();
+        }
+    }
+
+    protected void endCurrentAnimation() {
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.cancel();
+            mCurrentAnimation = null;
+        }
+    }
+
+    protected void setCurrentView(View v) {
+        mCurrentView = v;
+        mShift = 0;
+        mTargetView = null;
+    }
+
+    /**
+     * Gets the position of {@param v} relative to {@link #mContainer}.
+     */
+    public abstract void viewToRect(View v, Rect outRect);
+
+    private class ViewSetListener extends AnimatorListenerAdapter {
+        private final View mViewToSet;
+        private final boolean mCallOnCancel;
+        private boolean mCalled = false;
+
+        public ViewSetListener(View v, boolean callOnCancel) {
+            mViewToSet = v;
+            mCallOnCancel = callOnCancel;
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            if (!mCallOnCancel) {
+                mCalled = true;
+            }
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            if (!mCalled) {
+                setCurrentView(mViewToSet);
+                mCalled = true;
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/keyboard/FocusedItemDecorator.java b/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
new file mode 100644
index 0000000..9c80b0f
--- /dev/null
+++ b/src/com/android/launcher3/keyboard/FocusedItemDecorator.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.keyboard;
+
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.ItemDecoration;
+import android.support.v7.widget.RecyclerView.State;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+
+/**
+ * {@link ItemDecoration} for drawing and animating focused view background.
+ */
+public class FocusedItemDecorator extends ItemDecoration {
+
+    private FocusIndicatorHelper mHelper;
+
+    public FocusedItemDecorator(View container) {
+        mHelper = new FocusIndicatorHelper(container) {
+
+            @Override
+            public void viewToRect(View v, Rect outRect) {
+                outRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+            }
+        };
+    }
+
+    public OnFocusChangeListener getFocusListener() {
+        return mHelper;
+    }
+
+    @Override
+    public void onDraw(Canvas c, RecyclerView parent, State state) {
+        mHelper.draw(c);
+    }
+}
diff --git a/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
new file mode 100644
index 0000000..bd5c06e
--- /dev/null
+++ b/src/com/android/launcher3/keyboard/ViewGroupFocusHelper.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.keyboard;
+
+import android.graphics.Rect;
+import android.view.View;
+import android.view.View.OnFocusChangeListener;
+
+import com.android.launcher3.PagedView;
+
+/**
+ * {@link FocusIndicatorHelper} for a generic view group.
+ */
+public class ViewGroupFocusHelper extends FocusIndicatorHelper {
+
+    private final View mContainer;
+
+    public ViewGroupFocusHelper(View container) {
+        super(container);
+        mContainer = container;
+    }
+
+    @Override
+    public void viewToRect(View v, Rect outRect) {
+        outRect.left = 0;
+        outRect.top = 0;
+
+        computeLocationRelativeToContainer(v, outRect);
+
+        // If a view is scaled, its position will also shift accordingly. For optimization, only
+        // consider this for the last node.
+        outRect.left += (1 - v.getScaleX()) * v.getWidth() / 2;
+        outRect.top += (1 - v.getScaleY()) * v.getHeight() / 2;
+
+        outRect.right = outRect.left + (int) (v.getScaleX() * v.getWidth());
+        outRect.bottom = outRect.top + (int) (v.getScaleY() * v.getHeight());
+    }
+
+    private void computeLocationRelativeToContainer(View child, Rect outRect) {
+        View parent = (View) child.getParent();
+        outRect.left += child.getLeft();
+        outRect.top += child.getTop();
+
+        if (parent != mContainer) {
+            if (parent instanceof PagedView) {
+                PagedView page = (PagedView) parent;
+                outRect.left -= page.getScrollForPage(page.indexOfChild(child));
+            }
+
+            computeLocationRelativeToContainer(parent, outRect);
+        }
+    }
+
+    /**
+     * Sets the alpha of this FocusIndicatorHelper to 0 when a view with this listener
+     * receives focus.
+     */
+    public View.OnFocusChangeListener getHideIndicatorOnFocusListener() {
+        return new OnFocusChangeListener() {
+            @Override
+            public void onFocusChange(View v, boolean hasFocus) {
+                if (hasFocus) {
+                    endCurrentAnimation();
+                    setCurrentView(null);
+                    setAlpha(0);
+                    invalidateDirty();
+                }
+            }
+        };
+    }
+}
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
new file mode 100644
index 0000000..8629e92
--- /dev/null
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -0,0 +1,225 @@
+package com.android.launcher3.logging;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.ProviderConfig;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.text.DateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Wrapper around {@link Log} to allow writing to a file.
+ * This class can safely be called from main thread.
+ *
+ * Note: This should only be used for logging errors which have a persistent effect on user's data,
+ * but whose effect may not be visible immediately.
+ */
+public final class FileLog {
+
+    private static final String FILE_NAME_PREFIX = "log-";
+    private static final DateFormat DATE_FORMAT =
+            DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
+
+    private static final long MAX_LOG_FILE_SIZE = 4 << 20;  // 4 mb
+
+    private static Handler sHandler = null;
+    private static File sLogsDirectory = null;
+
+    public static void setDir(File logsDir) {
+        if (ProviderConfig.IS_DOGFOOD_BUILD) {
+            synchronized (DATE_FORMAT) {
+                // If the target directory changes, stop any active thread.
+                if (sHandler != null && !logsDir.equals(sLogsDirectory)) {
+                    ((HandlerThread) sHandler.getLooper().getThread()).quit();
+                    sHandler = null;
+                }
+            }
+        }
+        sLogsDirectory = logsDir;
+    }
+
+    public static void d(String tag, String msg, Exception e) {
+        Log.d(tag, msg, e);
+        print(tag, msg, e);
+    }
+
+    public static void d(String tag, String msg) {
+        Log.d(tag, msg);
+        print(tag, msg);
+    }
+
+    public static void e(String tag, String msg, Exception e) {
+        Log.e(tag, msg, e);
+        print(tag, msg, e);
+    }
+
+    public static void e(String tag, String msg) {
+        Log.e(tag, msg);
+        print(tag, msg);
+    }
+
+    public static void print(String tag, String msg) {
+        print(tag, msg, null);
+    }
+
+    public static void print(String tag, String msg, Exception e) {
+        if (!ProviderConfig.IS_DOGFOOD_BUILD) {
+            return;
+        }
+        String out = String.format("%s %s %s", DATE_FORMAT.format(new Date()), tag, msg);
+        if (e != null) {
+            out += "\n" + Log.getStackTraceString(e);
+        }
+        Message.obtain(getHandler(), LogWriterCallback.MSG_WRITE, out).sendToTarget();
+    }
+
+    private static Handler getHandler() {
+        synchronized (DATE_FORMAT) {
+            if (sHandler == null) {
+                HandlerThread thread = new HandlerThread("file-logger");
+                thread.start();
+                sHandler = new Handler(thread.getLooper(), new LogWriterCallback());
+            }
+        }
+        return sHandler;
+    }
+
+    /**
+     * Blocks until all the pending logs are written to the disk
+     * @param out if not null, all the persisted logs are copied to the writer.
+     */
+    public static void flushAll(PrintWriter out) throws InterruptedException {
+        if (!ProviderConfig.IS_DOGFOOD_BUILD) {
+            return;
+        }
+        CountDownLatch latch = new CountDownLatch(1);
+        Message.obtain(getHandler(), LogWriterCallback.MSG_FLUSH,
+                Pair.create(out, latch)).sendToTarget();
+
+        latch.await(2, TimeUnit.SECONDS);
+    }
+
+    /**
+     * Writes logs to the file.
+     * Log files are named log-0 for even days of the year and log-1 for odd days of the year.
+     * Logs older than 36 hours are purged.
+     */
+    private static class LogWriterCallback implements Handler.Callback {
+
+        private static final long CLOSE_DELAY = 5000;  // 5 seconds
+
+        private static final int MSG_WRITE = 1;
+        private static final int MSG_CLOSE = 2;
+        private static final int MSG_FLUSH = 3;
+
+        private String mCurrentFileName = null;
+        private PrintWriter mCurrentWriter = null;
+
+        private void closeWriter() {
+            Utilities.closeSilently(mCurrentWriter);
+            mCurrentWriter = null;
+        }
+
+        @Override
+        public boolean handleMessage(Message msg) {
+            if (sLogsDirectory == null || !ProviderConfig.IS_DOGFOOD_BUILD) {
+                return true;
+            }
+            switch (msg.what) {
+                case MSG_WRITE: {
+                    Calendar cal = Calendar.getInstance();
+                    // suffix with 0 or 1 based on the day of the year.
+                    String fileName = FILE_NAME_PREFIX + (cal.get(Calendar.DAY_OF_YEAR) & 1);
+
+                    if (!fileName.equals(mCurrentFileName)) {
+                        closeWriter();
+                    }
+
+                    try {
+                        if (mCurrentWriter == null) {
+                            mCurrentFileName = fileName;
+
+                            boolean append = false;
+                            File logFile = new File(sLogsDirectory, fileName);
+                            if (logFile.exists()) {
+                                Calendar modifiedTime = Calendar.getInstance();
+                                modifiedTime.setTimeInMillis(logFile.lastModified());
+
+                                // If the file was modified more that 36 hours ago, purge the file.
+                                // We use instead of 24 to account for day-365 followed by day-1
+                                modifiedTime.add(Calendar.HOUR, 36);
+                                append = cal.before(modifiedTime)
+                                        && logFile.length() < MAX_LOG_FILE_SIZE;
+                            }
+                            mCurrentWriter = new PrintWriter(new FileWriter(logFile, append));
+                        }
+
+                        mCurrentWriter.println((String) msg.obj);
+                        mCurrentWriter.flush();
+
+                        // Auto close file stream after some time.
+                        sHandler.removeMessages(MSG_CLOSE);
+                        sHandler.sendEmptyMessageDelayed(MSG_CLOSE, CLOSE_DELAY);
+                    } catch (Exception e) {
+                        Log.e("FileLog", "Error writing logs to file", e);
+                        // Close stream, will try reopening during next log
+                        closeWriter();
+                    }
+                    return true;
+                }
+                case MSG_CLOSE: {
+                    closeWriter();
+                    return true;
+                }
+                case MSG_FLUSH: {
+                    closeWriter();
+                    Pair<PrintWriter, CountDownLatch> p =
+                            (Pair<PrintWriter, CountDownLatch>) msg.obj;
+
+                    if (p.first != null) {
+                        dumpFile(p.first, FILE_NAME_PREFIX + 0);
+                        dumpFile(p.first, FILE_NAME_PREFIX + 1);
+                    }
+                    p.second.countDown();
+                    return true;
+                }
+            }
+            return true;
+        }
+    }
+
+    private static void dumpFile(PrintWriter out, String fileName) {
+        File logFile = new File(sLogsDirectory, fileName);
+        if (logFile.exists()) {
+
+            BufferedReader in = null;
+            try {
+                in = new BufferedReader(new FileReader(logFile));
+                out.println();
+                out.println("--- logfile: " + fileName + " ---");
+                String line;
+                while ((line = in.readLine()) != null) {
+                    out.println(line);
+                }
+            } catch (Exception e) {
+                // ignore
+            } finally {
+                Utilities.closeSilently(in);
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
new file mode 100644
index 0000000..c2b97eb
--- /dev/null
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -0,0 +1,248 @@
+package com.android.launcher3.logging;
+
+import android.view.View;
+
+import com.android.launcher3.ButtonDropTarget;
+import com.android.launcher3.DeleteDropTarget;
+import com.android.launcher3.InfoDropTarget;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.UninstallDropTarget;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+
+/**
+ * Debugging helper methods.
+ * toString() cannot be overriden inside auto generated {@link LauncherLogProto}.
+ * Note: switch statement cannot be replaced with reflection as proguard strips the constants
+ */
+public class LoggerUtils {
+    private static final String TAG = "LoggerUtils";
+
+    public static String getActionStr(LauncherLogProto.Action action) {
+        switch(action.touch) {
+            case Action.TAP: return "TAP";
+            case Action.LONGPRESS: return "LONGPRESS";
+            case Action.DRAGDROP: return "DRAGDROP";
+            case Action.PINCH: return "PINCH";
+            case Action.SWIPE: return "SWIPE";
+            case Action.FLING: return "FLING";
+            default: return "UNKNOWN";
+        }
+    }
+
+    public static String getTargetStr(Target t) {
+        String typeStr = "";
+        if (t == null){
+            return typeStr;
+        }
+        switch (t.type) {
+            case Target.ITEM:
+                return getItemStr(t);
+            case Target.CONTROL:
+                return getControlStr(t);
+            case Target.CONTAINER:
+                return getContainerStr(t);
+            default:
+                return "UNKNOWN TARGET TYPE";
+        }
+    }
+
+    private static String getItemStr(Target t) {
+        String typeStr = "";
+        if (t == null){
+            return typeStr;
+        }
+        switch(t.itemType){
+            case LauncherLogProto.APP_ICON: typeStr = "APPICON"; break;
+            case LauncherLogProto.SHORTCUT: typeStr = "SHORTCUT"; break;
+            case LauncherLogProto.WIDGET: typeStr = "WIDGET"; break;
+            case LauncherLogProto.DEEPSHORTCUT: typeStr = "DEEPSHORTCUT"; break;
+            case LauncherLogProto.FOLDER_ICON: typeStr = "FOLDERICON"; break;
+            case LauncherLogProto.SEARCHBOX: typeStr = "SEARCHBOX"; break;
+
+            default: typeStr = "UNKNOWN";
+        }
+
+        if (t.packageNameHash != 0) {
+            typeStr += ", packageHash=" + t.packageNameHash;
+        }
+        if (t.componentHash != 0) {
+            typeStr += ", componentHash=" + t.componentHash;
+        }
+        if (t.intentHash != 0) {
+            typeStr += ", intentHash=" + t.intentHash;
+        }
+        if (t.spanX != 0) {
+            typeStr += ", spanX=" + t.spanX;
+        }
+        return typeStr += ", grid=(" + t.gridX + "," + t.gridY + "), id=" + t.pageIndex;
+    }
+
+    private static String getControlStr(Target t) {
+        if (t == null){
+            return "";
+        }
+        switch(t.controlType) {
+            case LauncherLogProto.ALL_APPS_BUTTON: return "ALL_APPS_BUTTON";
+            case LauncherLogProto.WIDGETS_BUTTON: return "WIDGETS_BUTTON";
+            case LauncherLogProto.WALLPAPER_BUTTON: return "WALLPAPER_BUTTON";
+            case LauncherLogProto.SETTINGS_BUTTON: return "SETTINGS_BUTTON";
+            case LauncherLogProto.REMOVE_TARGET: return "REMOVE_TARGET";
+            case LauncherLogProto.UNINSTALL_TARGET: return "UNINSTALL_TARGET";
+            case LauncherLogProto.APPINFO_TARGET: return "APPINFO_TARGET";
+            case LauncherLogProto.RESIZE_HANDLE: return "RESIZE_HANDLE";
+            default: return "UNKNOWN";
+        }
+    }
+
+    private static String getContainerStr(LauncherLogProto.Target t) {
+        String str = "";
+        if (t == null) {
+            return str;
+        }
+        switch (t.containerType) {
+            case LauncherLogProto.WORKSPACE:
+                str = "WORKSPACE";
+                break;
+            case LauncherLogProto.HOTSEAT:
+                str = "HOTSEAT";
+                break;
+            case LauncherLogProto.FOLDER:
+                str = "FOLDER";
+                break;
+            case LauncherLogProto.ALLAPPS:
+                str = "ALLAPPS";
+                break;
+            case LauncherLogProto.WIDGETS:
+                str = "WIDGETS";
+                break;
+            case LauncherLogProto.OVERVIEW:
+                str = "OVERVIEW";
+                break;
+            case LauncherLogProto.PREDICTION:
+                str = "PREDICTION";
+                break;
+            case LauncherLogProto.SEARCHRESULT:
+                str = "SEARCHRESULT";
+                break;
+            case LauncherLogProto.DEEPSHORTCUTS:
+                str = "DEEPSHORTCUTS";
+                break;
+            default:
+                str = "UNKNOWN";
+        }
+        return str + " id=" + t.pageIndex;
+    }
+
+    /**
+     * Used for launching an event by tapping on an icon.
+     */
+    public static LauncherLogProto.LauncherEvent initLauncherEvent(
+            int actionType,
+            View v,
+            int parentTargetType){
+        LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent();
+
+        event.srcTarget = new LauncherLogProto.Target[2];
+        event.srcTarget[0] = initTarget(v);
+        event.srcTarget[1] = new LauncherLogProto.Target();
+        event.srcTarget[1].type = parentTargetType;
+
+        event.action = new LauncherLogProto.Action();
+        event.action.type = actionType;
+        return event;
+    }
+
+    /**
+     * Used for clicking on controls and buttons.
+     */
+    public static LauncherLogProto.LauncherEvent initLauncherEvent(
+            int actionType,
+            int childTargetType){
+        LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent();
+
+        event.srcTarget = new LauncherLogProto.Target[1];
+        event.srcTarget[0] = new LauncherLogProto.Target();
+        event.srcTarget[0].type = childTargetType;
+
+        event.action = new LauncherLogProto.Action();
+        event.action.type = actionType;
+        return event;
+    }
+
+    /**
+     * Used for drag and drop interaction.
+     */
+    public static LauncherLogProto.LauncherEvent initLauncherEvent(
+            int actionType,
+            View v,
+            ItemInfo info,
+            int parentSrcTargetType,
+            View parentDestTargetType){
+        LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent();
+
+        event.srcTarget = new LauncherLogProto.Target[2];
+        event.srcTarget[0] = initTarget(v, info);
+        event.srcTarget[1] = new LauncherLogProto.Target();
+        event.srcTarget[1].type = parentSrcTargetType;
+
+        event.destTarget = new LauncherLogProto.Target[2];
+        event.destTarget[0] = initTarget(v, info);
+        event.destTarget[1] = initDropTarget(parentDestTargetType);
+
+        event.action = new LauncherLogProto.Action();
+        event.action.type = actionType;
+        return event;
+    }
+
+    private static Target initTarget(View v, ItemInfo info) {
+        Target t = new LauncherLogProto.Target();
+        t.type = Target.ITEM;
+        switch (info.itemType) {
+            case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+                t.itemType = LauncherLogProto.APP_ICON;
+                break;
+            case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+                t.itemType = LauncherLogProto.SHORTCUT;
+                break;
+            case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
+                t.itemType = LauncherLogProto.FOLDER_ICON;
+                break;
+            case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                t.itemType = LauncherLogProto.WIDGET;
+                break;
+            case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+                t.itemType = LauncherLogProto.DEEPSHORTCUT;
+                break;
+        }
+        return t;
+    }
+
+    private static Target initDropTarget(View v) {
+        Target t = new LauncherLogProto.Target();
+        t.type = (v instanceof ButtonDropTarget)? Target.CONTROL : Target.CONTAINER;
+        if (t.type == Target.CONTAINER) {
+            return t;
+        }
+
+        if (v instanceof InfoDropTarget) {
+            t.controlType = LauncherLogProto.APPINFO_TARGET;
+        } else if (v instanceof UninstallDropTarget) {
+            t.controlType = LauncherLogProto.UNINSTALL_TARGET;
+        } else if (v instanceof DeleteDropTarget) {
+            t.controlType = LauncherLogProto.REMOVE_TARGET;
+        }
+        return t;
+    }
+
+    private static Target initTarget(View v) {
+        Target t = new LauncherLogProto.Target();
+        t.type = Target.ITEM;
+        if (!(v.getTag() instanceof ItemInfo)) {
+            return t;
+        }
+        return initTarget(v, (ItemInfo) v.getTag());
+    }
+}
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
new file mode 100644
index 0000000..56fdce8
--- /dev/null
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.logging;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewParent;
+
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.util.ComponentKey;
+
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Manages the creation of {@link LauncherEvent}.
+ * To debug this class, execute following command before sideloading a new apk.
+ *
+ * $ adb shell setprop log.tag.UserEvent VERBOSE
+ */
+public class UserEventDispatcher {
+
+    private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
+
+    private final boolean mIsVerbose;
+
+    /**
+     * TODO: change the name of this interface to LogContainerProvider
+     * and the method name to fillInLogContainerData. Not changed to minimize CL diff
+     * in this branch.
+     *
+     * Implemented by containers to provide a launch source for a given child.
+     */
+    public interface LaunchSourceProvider {
+
+        /**
+         * Copies data from the source to the destination proto.
+         *
+         * @param v            source of the data
+         * @param info         source of the data
+         * @param target       dest of the data
+         * @param targetParent dest of the data
+         */
+        void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent);
+    }
+
+    /**
+     * Recursively finds the parent of the given child which implements IconLogInfoProvider
+     */
+    public static LaunchSourceProvider getLaunchProviderRecursive(View v) {
+        ViewParent parent = null;
+
+        if (v != null) {
+            parent = v.getParent();
+        } else {
+            return null;
+        }
+
+        // Optimization to only check up to 5 parents.
+        int count = MAXIMUM_VIEW_HIERARCHY_LEVEL;
+        while (parent != null && count-- > 0) {
+            if (parent instanceof LaunchSourceProvider) {
+                return (LaunchSourceProvider) parent;
+            } else {
+                parent = parent.getParent();
+            }
+        }
+        return null;
+    }
+
+    private String TAG = "UserEvent";
+
+    private long mElapsedContainerMillis;
+    private long mElapsedSessionMillis;
+    private long mActionDurationMillis;
+
+    // Used for filling in predictedRank on {@link Target}s.
+    private List<ComponentKey> mPredictedApps;
+
+    public UserEventDispatcher() {
+        if (ProviderConfig.IS_DOGFOOD_BUILD) {
+            mIsVerbose = Utilities.isPropertyEnabled(TAG);
+        } else {
+            mIsVerbose = false;
+        }
+    }
+
+    //                      APP_ICON    SHORTCUT    WIDGET
+    // --------------------------------------------------------------
+    // packageNameHash      required    optional    required
+    // componentNameHash    required                required
+    // intentHash                       required
+    // --------------------------------------------------------------
+
+    protected LauncherEvent createLauncherEvent(View v, Intent intent) {
+        LauncherEvent event = LoggerUtils.initLauncherEvent(
+                Action.TOUCH, v, Target.CONTAINER);
+        event.action.touch = Action.TAP;
+
+        // Fill in grid(x,y), pageIndex of the child and container type of the parent
+        // TODO: make this percolate up the view hierarchy if needed.
+        int idx = 0;
+        LaunchSourceProvider provider = getLaunchProviderRecursive(v);
+        if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
+            return null;
+        }
+        ItemInfo itemInfo = (ItemInfo) v.getTag();
+        provider.fillInLaunchSourceData(v, itemInfo, event.srcTarget[idx], event.srcTarget[idx + 1]);
+
+        event.srcTarget[idx].intentHash = intent.hashCode();
+        ComponentName cn = intent.getComponent();
+        if (cn != null) {
+            event.srcTarget[idx].packageNameHash = cn.getPackageName().hashCode();
+            event.srcTarget[idx].componentHash = cn.hashCode();
+            if (mPredictedApps != null) {
+                event.srcTarget[idx].predictedRank = mPredictedApps.indexOf(
+                        new ComponentKey(cn, itemInfo.user));
+            }
+        }
+        return event;
+    }
+
+    public void logAppLaunch(View v, Intent intent) {
+        LauncherEvent ev = createLauncherEvent(v, intent);
+        if (ev == null) {
+            return;
+        }
+        dispatchUserEvent(ev, intent);
+    }
+
+    public void logActionOnItem(int action, int itemType) {
+        LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH, Target.ITEM);
+        event.action.touch = action;
+        event.srcTarget[0].itemType = itemType;
+        dispatchUserEvent(event, null);
+    }
+
+    public void logActionOnControl(int action, int controlType) {
+        LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH, Target.CONTROL);
+        event.action.touch = action;
+        event.srcTarget[0].controlType = controlType;
+        dispatchUserEvent(event, null);
+    }
+
+    public void logActionOnContainer(int action, int dir, int containerType) {
+        LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH, Target.CONTAINER);
+        event.action.touch = action;
+        event.action.dir = dir;
+        event.srcTarget[0].containerType = containerType;
+        dispatchUserEvent(event, null);
+    }
+
+    public void logDeepShortcutsOpen(View icon) {
+        LauncherEvent event = LoggerUtils.initLauncherEvent(
+                Action.TOUCH, icon, Target.CONTAINER);
+        LaunchSourceProvider provider = getLaunchProviderRecursive(icon);
+        if (icon == null && !(icon.getTag() instanceof ItemInfo)) {
+            return;
+        }
+        ItemInfo info = (ItemInfo) icon.getTag();
+        provider.fillInLaunchSourceData(icon, info, event.srcTarget[0], event.srcTarget[1]);
+        event.action.touch = Action.LONGPRESS;
+        dispatchUserEvent(event, null);
+    }
+
+    public void setPredictedApps(List<ComponentKey> predictedApps) {
+        mPredictedApps = predictedApps;
+    }
+
+    public void logDragNDrop(DropTarget.DragObject dragObj, View dropTargetAsView) {
+        LauncherEvent event = LoggerUtils.initLauncherEvent(Action.TOUCH,
+                dragObj.dragView,
+                dragObj.originalDragInfo,
+                Target.CONTAINER,
+                dropTargetAsView);
+        event.action.touch = Action.DRAGDROP;
+
+        dragObj.dragSource.fillInLaunchSourceData(null, dragObj.originalDragInfo,
+                event.srcTarget[0], event.srcTarget[1]);
+
+        if (dropTargetAsView instanceof LaunchSourceProvider) {
+            ((LaunchSourceProvider) dropTargetAsView).fillInLaunchSourceData(null,
+                    dragObj.dragInfo, event.destTarget[0], event.destTarget[1]);
+
+        }
+        event.actionDurationMillis = SystemClock.uptimeMillis() - mActionDurationMillis;
+        dispatchUserEvent(event, null);
+    }
+
+    /**
+     * Currently logs following containers: workspace, allapps, widget tray.
+     */
+    public final void resetElapsedContainerMillis() {
+        mElapsedContainerMillis = SystemClock.uptimeMillis();
+    }
+
+    public final void resetElapsedSessionMillis() {
+        mElapsedSessionMillis = SystemClock.uptimeMillis();
+        mElapsedContainerMillis = SystemClock.uptimeMillis();
+    }
+
+    public final void resetActionDurationMillis() {
+        mActionDurationMillis = SystemClock.uptimeMillis();
+    }
+
+    public void dispatchUserEvent(LauncherEvent ev, Intent intent) {
+        ev.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
+        ev.elapsedSessionMillis = SystemClock.uptimeMillis() - mElapsedSessionMillis;
+
+        if (!mIsVerbose) {
+            return;
+        }
+        Log.d(TAG, String.format(Locale.US,
+                "\naction:%s\n Source child:%s\tparent:%s",
+                LoggerUtils.getActionStr(ev.action),
+                LoggerUtils.getTargetStr(ev.srcTarget != null ? ev.srcTarget[0] : null),
+                LoggerUtils.getTargetStr(ev.srcTarget != null && ev.srcTarget.length > 1 ?
+                        ev.srcTarget[1] : null)));
+        if (ev.destTarget != null && ev.destTarget.length > 0) {
+            Log.d(TAG, String.format(Locale.US,
+                    " Destination child:%s\tparent:%s",
+                    LoggerUtils.getTargetStr(ev.destTarget != null ? ev.destTarget[0] : null),
+                    LoggerUtils.getTargetStr(ev.destTarget != null && ev.destTarget.length > 1 ?
+                            ev.destTarget[1] : null)));
+        }
+        Log.d(TAG, String.format(Locale.US,
+                " Elapsed container %d ms session %d ms action %d ms",
+                ev.elapsedContainerMillis,
+                ev.elapsedSessionMillis,
+                ev.actionDurationMillis));
+    }
+}
diff --git a/src/com/android/launcher3/logging/UserEventLogger.java b/src/com/android/launcher3/logging/UserEventLogger.java
deleted file mode 100644
index d05b683..0000000
--- a/src/com/android/launcher3/logging/UserEventLogger.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.android.launcher3.logging;
-
-public abstract class UserEventLogger {
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 8117122..fd647c7 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -7,6 +7,7 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.graphics.Point;
 import android.net.Uri;
@@ -22,9 +23,11 @@
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.backup.nano.BackupProtos;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.GridOccupancy;
 import com.android.launcher3.util.LongArrayMap;
 
 import java.util.ArrayList;
@@ -45,11 +48,7 @@
     private static final boolean DEBUG = true;
 
     private static final String KEY_MIGRATION_SRC_WORKSPACE_SIZE = "migration_src_workspace_size";
-    private static final String KEY_MIGRATION_SRC_HOTSEAT_SIZE = "migration_src_hotseat_size";
-
-    // Set of entries indicating minimum size a widget can be resized to. This is used during
-    // restore in case the widget has not been installed yet.
-    private static final String KEY_MIGRATION_WIDGET_MINSIZE = "migration_widget_min_size";
+    private static final String KEY_MIGRATION_SRC_HOTSEAT_COUNT = "migration_src_hotseat_count";
 
     // These are carefully selected weights for various item types (Math.random?), to allow for
     // the least absurd migration experience.
@@ -64,9 +63,9 @@
 
     private final HashMap<String, Point> mWidgetMinSize = new HashMap<>();
     private final ContentValues mTempValues = new ContentValues();
-    private final ArrayList<Long> mEntryToRemove = new ArrayList<>();
+    protected final ArrayList<Long> mEntryToRemove = new ArrayList<>();
     private final ArrayList<ContentProviderOperation> mUpdateOperations = new ArrayList<>();
-    private final ArrayList<DbEntry> mCarryOver = new ArrayList<>();
+    protected final ArrayList<DbEntry> mCarryOver = new ArrayList<>();
     private final HashSet<String> mValidPackages;
 
     private final int mSrcX, mSrcY;
@@ -74,16 +73,12 @@
     private final boolean mShouldRemoveX, mShouldRemoveY;
 
     private final int mSrcHotseatSize;
-    private final int mSrcAllAppsRank;
     private final int mDestHotseatSize;
-    private final int mDestAllAppsRank;
 
     protected GridSizeMigrationTask(Context context, InvariantDeviceProfile idp,
-            HashSet<String> validPackages, HashMap<String, Point> widgetMinSize,
-            Point sourceSize, Point targetSize) {
+            HashSet<String> validPackages, Point sourceSize, Point targetSize) {
         mContext = context;
         mValidPackages = validPackages;
-        mWidgetMinSize.putAll(widgetMinSize);
         mIdp = idp;
 
         mSrcX = sourceSize.x;
@@ -96,22 +91,19 @@
         mShouldRemoveY = mTrgY < mSrcY;
 
         // Non-used variables
-        mSrcHotseatSize = mSrcAllAppsRank = mDestHotseatSize = mDestAllAppsRank = -1;
+        mSrcHotseatSize = mDestHotseatSize = -1;
     }
 
     protected GridSizeMigrationTask(Context context,
             InvariantDeviceProfile idp, HashSet<String> validPackages,
-            int srcHotseatSize, int srcAllAppsRank,
-            int destHotseatSize, int destAllAppsRank) {
+            int srcHotseatSize, int destHotseatSize) {
         mContext = context;
         mIdp = idp;
         mValidPackages = validPackages;
 
         mSrcHotseatSize = srcHotseatSize;
-        mSrcAllAppsRank = srcAllAppsRank;
 
         mDestHotseatSize = destHotseatSize;
-        mDestAllAppsRank = destAllAppsRank;
 
         // Non-used variables
         mSrcX = mSrcY = mTrgX = mTrgY = -1;
@@ -151,7 +143,7 @@
     protected boolean migrateHotseat() throws Exception {
         ArrayList<DbEntry> items = loadHotseatEntries();
 
-        int requiredCount = mDestHotseatSize - 1;
+        int requiredCount = FeatureFlags.NO_ALL_APPS_ICON ? mDestHotseatSize : mDestHotseatSize - 1;
 
         while (items.size() > requiredCount) {
             // Pick the center item by default.
@@ -183,7 +175,7 @@
             }
 
             newScreenId++;
-            if (newScreenId == mDestAllAppsRank) {
+            if (!FeatureFlags.NO_ALL_APPS_ICON && mIdp.isAllAppsButtonRank(newScreenId)) {
                 newScreenId++;
             }
         }
@@ -220,10 +212,14 @@
                 // {@link #mCarryOver}, to prevent an infinite loop. If no item could be removed,
                 // break the loop and abort migration by throwing an exception.
                 OptimalPlacementSolution placement = new OptimalPlacementSolution(
-                        new boolean[mTrgX][mTrgY], deepCopy(mCarryOver), true);
+                        new GridOccupancy(mTrgX, mTrgY), deepCopy(mCarryOver), 0, true);
                 placement.find();
                 if (placement.finalPlacedItems.size() > 0) {
-                    long newScreenId = LauncherAppState.getLauncherProvider().generateNewScreenId();
+                    long newScreenId = LauncherSettings.Settings.call(
+                            mContext.getContentResolver(),
+                            LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
+                            .getLong(LauncherSettings.Settings.EXTRA_VALUE);
+
                     allScreens.add(newScreenId);
                     for (DbEntry item : placement.finalPlacedItems) {
                         if (!mCarryOver.remove(itemMap.get(item.id))) {
@@ -257,13 +253,17 @@
      * Migrate a particular screen id.
      * Strategy:
      *   1) For all possible combinations of row and column, pick the one which causes the least
-     *      data loss: {@link #tryRemove(int, int, ArrayList, float[])}
+     *      data loss: {@link #tryRemove(int, int, int, ArrayList, float[])}
      *   2) Maintain a list of all lost items before this screen, and add any new item lost from
      *      this screen to that list as well.
      *   3) If all those items from the above list can be placed on this screen, place them
      *      (otherwise they are placed on a new screen).
      */
-    private void migrateScreen(long screenId) {
+    protected void migrateScreen(long screenId) {
+        // If we are migrating the first screen, do not touch the first row.
+        int startY = (FeatureFlags.QSB_ON_FIRST_SCREEN && screenId == Workspace.FIRST_SCREEN_ID)
+                ? 1 : 0;
+
         ArrayList<DbEntry> items = loadWorkspaceEntries(screenId);
 
         int removedCol = Integer.MAX_VALUE;
@@ -281,10 +281,12 @@
 
         // Try removing all possible combinations
         for (int x = 0; x < mSrcX; x++) {
-            for (int y = 0; y < mSrcY; y++) {
+            // Try removing the rows first from bottom. This keeps the workspace
+            // nicely aligned with hotseat.
+            for (int y = mSrcY - 1; y >= startY; y--) {
                 // Use a deep copy when trying out a particular combination as it can change
                 // the underlying object.
-                ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, deepCopy(items), outLoss);
+                ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, startY, deepCopy(items), outLoss);
 
                 if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1] < moveWt))) {
                     removeWt = outLoss[0];
@@ -332,13 +334,14 @@
 
         if (!mCarryOver.isEmpty() && removeWt == 0) {
             // No new items were removed in this step. Try placing all the items on this screen.
-            boolean[][] occupied = new boolean[mTrgX][mTrgY];
+            GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
+            occupied.markCells(0, 0, mTrgX, startY, true);
             for (DbEntry item : finalItems) {
-                markCells(occupied, item, true);
+                occupied.markCells(item, true);
             }
 
             OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied,
-                    deepCopy(mCarryOver), true);
+                    deepCopy(mCarryOver), startY, true);
             placement.find();
             if (placement.lowestWeightLoss == 0) {
                 // All items got placed
@@ -356,7 +359,7 @@
     /**
      * Updates an item in the DB.
      */
-    private void update(DbEntry item) {
+    protected void update(DbEntry item) {
         mTempValues.clear();
         item.addToContentValues(mTempValues);
         mUpdateOperations.add(ContentProviderOperation
@@ -370,9 +373,10 @@
      * @param outLoss array of size 2. The first entry is filled with weight loss, and the second
      * with the overall item movement.
      */
-    private ArrayList<DbEntry> tryRemove(int col, int row, ArrayList<DbEntry> items,
-            float[] outLoss) {
-        boolean[][] occupied = new boolean[mTrgX][mTrgY];
+    private ArrayList<DbEntry> tryRemove(int col, int row, int startY,
+            ArrayList<DbEntry> items, float[] outLoss) {
+        GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
+        occupied.markCells(0, 0, mTrgX, startY, true);
 
         col = mShouldRemoveX ? col : Integer.MAX_VALUE;
         row = mShouldRemoveY ? row : Integer.MAX_VALUE;
@@ -390,11 +394,12 @@
                 if (item.cellX > col) item.cellX --;
                 if (item.cellY > row) item.cellY --;
                 finalItems.add(item);
-                markCells(occupied, item, true);
+                occupied.markCells(item, true);
             }
         }
 
-        OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied, removedItems);
+        OptimalPlacementSolution placement =
+                new OptimalPlacementSolution(occupied, removedItems, startY);
         placement.find();
         finalItems.addAll(placement.finalPlacedItems);
         outLoss[0] = placement.lowestWeightLoss;
@@ -402,49 +407,32 @@
         return finalItems;
     }
 
-    private void markCells(boolean[][] occupied, DbEntry item, boolean val) {
-        for (int i = item.cellX; i < (item.cellX + item.spanX); i++) {
-            for (int j = item.cellY; j < (item.cellY + item.spanY); j++) {
-                occupied[i][j] = val;
-            }
-        }
-    }
-
-    private boolean isVacant(boolean[][] occupied, int x, int y, int w, int h) {
-        if (x + w > mTrgX) return false;
-        if (y + h > mTrgY) return false;
-
-        for (int i = 0; i < w; i++) {
-            for (int j = 0; j < h; j++) {
-                if (occupied[i + x][j + y]) {
-                    return false;
-                }
-            }
-        }
-        return true;
-    }
-
     private class OptimalPlacementSolution {
         private final ArrayList<DbEntry> itemsToPlace;
-        private final boolean[][] occupied;
+        private final GridOccupancy occupied;
 
         // If set to true, item movement are not considered in move cost, leading to a more
         // linear placement.
         private final boolean ignoreMove;
 
+        // The first row in the grid from where the placement should start.
+        private final int startY;
+
         float lowestWeightLoss = Float.MAX_VALUE;
         float lowestMoveCost = Float.MAX_VALUE;
         ArrayList<DbEntry> finalPlacedItems;
 
-        public OptimalPlacementSolution(boolean[][] occupied, ArrayList<DbEntry> itemsToPlace) {
-            this(occupied, itemsToPlace, false);
+        public OptimalPlacementSolution(
+                GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace, int startY) {
+            this(occupied, itemsToPlace, startY, false);
         }
 
-        public OptimalPlacementSolution(boolean[][] occupied, ArrayList<DbEntry> itemsToPlace,
-                boolean ignoreMove) {
+        public OptimalPlacementSolution(GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace,
+                int startY, boolean ignoreMove) {
             this.occupied = occupied;
             this.itemsToPlace = itemsToPlace;
             this.ignoreMove = ignoreMove;
+            this.startY = startY;
 
             // Sort the items such that larger widgets appear first followed by 1x1 items
             Collections.sort(this.itemsToPlace);
@@ -494,7 +482,7 @@
                 int myW = me.spanX;
                 int myH = me.spanY;
 
-                for (int y = 0; y < mTrgY; y++) {
+                for (int y = startY; y < mTrgY; y++) {
                     for (int x = 0; x < mTrgX; x++) {
                         float newMoveCost = moveCost;
                         if (x != myX) {
@@ -509,42 +497,42 @@
                             newMoveCost = moveCost;
                         }
 
-                        if (isVacant(occupied, x, y, myW, myH)) {
+                        if (occupied.isRegionVacant(x, y, myW, myH)) {
                             // place at this position and continue search.
-                            markCells(occupied, me, true);
+                            occupied.markCells(me, true);
                             find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
-                            markCells(occupied, me, false);
+                            occupied.markCells(me, false);
                         }
 
                         // Try resizing horizontally
-                        if (myW > me.minSpanX && isVacant(occupied, x, y, myW - 1, myH)) {
+                        if (myW > me.minSpanX && occupied.isRegionVacant(x, y, myW - 1, myH)) {
                             me.spanX --;
-                            markCells(occupied, me, true);
+                            occupied.markCells(me, true);
                             // 1 extra move cost
                             find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
-                            markCells(occupied, me, false);
+                            occupied.markCells(me, false);
                             me.spanX ++;
                         }
 
                         // Try resizing vertically
-                        if (myH > me.minSpanY && isVacant(occupied, x, y, myW, myH - 1)) {
+                        if (myH > me.minSpanY && occupied.isRegionVacant(x, y, myW, myH - 1)) {
                             me.spanY --;
-                            markCells(occupied, me, true);
+                            occupied.markCells(me, true);
                             // 1 extra move cost
                             find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
-                            markCells(occupied, me, false);
+                            occupied.markCells(me, false);
                             me.spanY ++;
                         }
 
                         // Try resizing horizontally & vertically
                         if (myH > me.minSpanY && myW > me.minSpanX &&
-                                isVacant(occupied, x, y, myW - 1, myH - 1)) {
+                                occupied.isRegionVacant(x, y, myW - 1, myH - 1)) {
                             me.spanX --;
                             me.spanY --;
-                            markCells(occupied, me, true);
+                            occupied.markCells(me, true);
                             // 2 extra move cost
                             find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe);
-                            markCells(occupied, me, false);
+                            occupied.markCells(me, false);
                             me.spanX ++;
                             me.spanY ++;
                         }
@@ -564,9 +552,9 @@
                 int newDistance = Integer.MAX_VALUE;
                 int newX = Integer.MAX_VALUE, newY = Integer.MAX_VALUE;
 
-                for (int y = 0; y < mTrgY; y++) {
+                for (int y = startY; y < mTrgY; y++) {
                     for (int x = 0; x < mTrgX; x++) {
-                        if (!occupied[x][y]) {
+                        if (!occupied.cells[x][y]) {
                             int dist = ignoreMove ? 0 :
                                 ((me.cellX - x) * (me.cellX - x) + (me.cellY - y) * (me.cellY - y));
                             if (dist < newDistance) {
@@ -591,9 +579,9 @@
                     if (ignoreMove) {
                         newMoveCost = moveCost;
                     }
-                    markCells(occupied, me, true);
+                    occupied.markCells(me, true);
                     find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
-                    markCells(occupied, me, false);
+                    occupied.markCells(me, false);
                     me.cellX = myX;
                     me.cellY = myY;
 
@@ -648,10 +636,11 @@
                 // calculate weight
                 switch (entry.itemType) {
                     case Favorites.ITEM_TYPE_SHORTCUT:
+                    case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                     case Favorites.ITEM_TYPE_APPLICATION: {
                         verifyIntent(c.getString(indexIntent));
-                        entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT
-                                ? WT_SHORTCUT : WT_APPLICATION;
+                        entry.weight = entry.itemType == Favorites.ITEM_TYPE_APPLICATION ?
+                                WT_APPLICATION : WT_SHORTCUT;
                         break;
                     }
                     case Favorites.ITEM_TYPE_FOLDER: {
@@ -682,8 +671,8 @@
     /**
      * Loads entries for a particular screen id.
      */
-    private ArrayList<DbEntry> loadWorkspaceEntries(long screen) {
-        Cursor c =  mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+    protected ArrayList<DbEntry> loadWorkspaceEntries(long screen) {
+        Cursor c = queryWorkspace(
                 new String[]{
                         Favorites._ID,                  // 0
                         Favorites.ITEM_TYPE,            // 1
@@ -695,7 +684,7 @@
                         Favorites.APPWIDGET_PROVIDER,   // 7
                         Favorites.APPWIDGET_ID},        // 8
                 Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP
-                        + " AND " + Favorites.SCREEN + " = " + screen, null, null, null);
+                        + " AND " + Favorites.SCREEN + " = " + screen);
 
         final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
         final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
@@ -722,10 +711,11 @@
                 // calculate weight
                 switch (entry.itemType) {
                     case Favorites.ITEM_TYPE_SHORTCUT:
+                    case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                     case Favorites.ITEM_TYPE_APPLICATION: {
                         verifyIntent(c.getString(indexIntent));
-                        entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT
-                            ? WT_SHORTCUT : WT_APPLICATION;
+                        entry.weight = entry.itemType == Favorites.ITEM_TYPE_APPLICATION ?
+                                WT_APPLICATION : WT_SHORTCUT;
                         break;
                     }
                     case Favorites.ITEM_TYPE_APPWIDGET: {
@@ -781,9 +771,9 @@
      * @return the number of valid items in the folder.
      */
     private int getFolderItemsCount(long folderId) {
-        Cursor c =  mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+        Cursor c = queryWorkspace(
                 new String[]{Favorites._ID, Favorites.INTENT},
-                Favorites.CONTAINER + " = " + folderId, null, null, null);
+                Favorites.CONTAINER + " = " + folderId);
 
         int total = 0;
         while (c.moveToNext()) {
@@ -798,6 +788,11 @@
         return total;
     }
 
+    protected Cursor queryWorkspace(String[] columns, String where) {
+        return mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                columns, where, null, null, null);
+    }
+
     /**
      * Verifies if the intent should be restored.
      */
@@ -820,7 +815,7 @@
         }
     }
 
-    private static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
+    protected static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
 
         public float weight;
 
@@ -887,13 +882,10 @@
     }
 
     public static void markForMigration(
-            Context context, HashSet<String> widgets, BackupProtos.DeviceProfieData srcProfile) {
+            Context context, int gridX, int gridY, int hotseatSize) {
         Utilities.getPrefs(context).edit()
-                .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE,
-                        getPointString((int) srcProfile.desktopCols, (int) srcProfile.desktopRows))
-                .putString(KEY_MIGRATION_SRC_HOTSEAT_SIZE,
-                        getPointString((int) srcProfile.hotseatCount, srcProfile.allappsRank))
-                .putStringSet(KEY_MIGRATION_WIDGET_MINSIZE, widgets)
+                .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, getPointString(gridX, gridY))
+                .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, hotseatSize)
                 .apply();
     }
 
@@ -906,10 +898,9 @@
         InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
 
         String gridSizeString = getPointString(idp.numColumns, idp.numRows);
-        String hotseatSizeString = getPointString(idp.numHotseatIcons, idp.hotseatAllAppsRank);
 
         if (gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, "")) &&
-                hotseatSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_HOTSEAT_SIZE, ""))) {
+                idp.numHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)) {
             // Skip if workspace and hotseat sizes have not changed.
             return true;
         }
@@ -918,30 +909,15 @@
         try {
             boolean dbChanged = false;
 
-            // Initialize list of valid packages. This contain all the packages which are already on
-            // the device and packages which are being installed. Any item which doesn't belong to
-            // this set is removed.
-            // Since the loader removes such items anyway, removing these items here doesn't cause
-            // any extra data loss and gives us more free space on the grid for better migration.
-            HashSet validPackages = new HashSet<>();
-            for (PackageInfo info : context.getPackageManager().getInstalledPackages(0)) {
-                validPackages.add(info.packageName);
-            }
-            validPackages.addAll(PackageInstallerCompat.getInstance(context)
-                    .updateAndGetActiveSessionCache().keySet());
-
+            HashSet validPackages = getValidPackages(context);
             // Hotseat
-            Point srcHotseatSize = parsePoint(prefs.getString(
-                    KEY_MIGRATION_SRC_HOTSEAT_SIZE, hotseatSizeString));
-            if (srcHotseatSize.x != idp.numHotseatIcons ||
-                    srcHotseatSize.y != idp.hotseatAllAppsRank) {
+            int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons);
+            if (srcHotseatCount != idp.numHotseatIcons) {
                 // Migrate hotseat.
 
                 dbChanged = new GridSizeMigrationTask(context,
                         LauncherAppState.getInstance().getInvariantDeviceProfile(),
-                        validPackages,
-                        srcHotseatSize.x, srcHotseatSize.y,
-                        idp.numHotseatIcons, idp.hotseatAllAppsRank).migrateHotseat();
+                        validPackages, srcHotseatCount, idp.numHotseatIcons).migrateHotseat();
             }
 
             // Grid size
@@ -949,54 +925,8 @@
             Point sourceSize = parsePoint(prefs.getString(
                     KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
 
-            if (!targetSize.equals(sourceSize)) {
-
-                // The following list defines all possible grid sizes (and intermediate steps
-                // during migration). Note that at each step, dx <= 1 && dy <= 1. Any grid size
-                // which is not in this list is not migrated.
-                // Note that the InvariantDeviceProfile defines (rows, cols) but the Points
-                // specified here are defined as (cols, rows).
-                ArrayList<Point> gridSizeSteps = new ArrayList<>();
-                gridSizeSteps.add(new Point(3, 2));
-                gridSizeSteps.add(new Point(3, 3));
-                gridSizeSteps.add(new Point(4, 3));
-                gridSizeSteps.add(new Point(4, 4));
-                gridSizeSteps.add(new Point(5, 5));
-                gridSizeSteps.add(new Point(6, 5));
-                gridSizeSteps.add(new Point(6, 6));
-                gridSizeSteps.add(new Point(7, 7));
-
-                int sourceSizeIndex = gridSizeSteps.indexOf(sourceSize);
-                int targetSizeIndex = gridSizeSteps.indexOf(targetSize);
-
-                if (sourceSizeIndex <= -1 || targetSizeIndex <= -1) {
-                    throw new Exception("Unable to migrate grid size from " + sourceSize
-                            + " to " + targetSize);
-                }
-
-                // Min widget sizes
-                HashMap<String, Point> widgetMinSize = new HashMap<>();
-                for (String s : Utilities.getPrefs(context).getStringSet(KEY_MIGRATION_WIDGET_MINSIZE,
-                        Collections.<String>emptySet())) {
-                    String[] parts = s.split("#");
-                    widgetMinSize.put(parts[0], parsePoint(parts[1]));
-                }
-
-                // Migrate the workspace grid, step by step.
-                while (targetSizeIndex < sourceSizeIndex ) {
-                    // We only need to migrate the grid if source size is greater
-                    // than the target size.
-                    Point stepTargetSize = gridSizeSteps.get(sourceSizeIndex - 1);
-                    Point stepSourceSize = gridSizeSteps.get(sourceSizeIndex);
-
-                    if (new GridSizeMigrationTask(context,
-                            LauncherAppState.getInstance().getInvariantDeviceProfile(),
-                            validPackages, widgetMinSize,
-                            stepSourceSize, stepTargetSize).migrateWorkspace()) {
-                        dbChanged = true;
-                    }
-                    sourceSizeIndex--;
-                }
+            if (new MultiStepMigrationTask(validPackages, context).migrate(sourceSize, targetSize)) {
+                dbChanged = true;
             }
 
             if (dbChanged) {
@@ -1022,9 +952,95 @@
             // Save current configuration, so that the migration does not run again.
             prefs.edit()
                     .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
-                    .putString(KEY_MIGRATION_SRC_HOTSEAT_SIZE, hotseatSizeString)
-                    .remove(KEY_MIGRATION_WIDGET_MINSIZE)
+                    .putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
                     .apply();
         }
     }
+
+    protected static HashSet<String> getValidPackages(Context context) {
+        // Initialize list of valid packages. This contain all the packages which are already on
+        // the device and packages which are being installed. Any item which doesn't belong to
+        // this set is removed.
+        // Since the loader removes such items anyway, removing these items here doesn't cause
+        // any extra data loss and gives us more free space on the grid for better migration.
+        HashSet validPackages = new HashSet<>();
+        for (PackageInfo info : context.getPackageManager()
+                .getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES)) {
+            validPackages.add(info.packageName);
+        }
+        validPackages.addAll(PackageInstallerCompat.getInstance(context)
+                .updateAndGetActiveSessionCache().keySet());
+        return validPackages;
+    }
+
+    /**
+     * Removes any broken item from the hotseat.
+     * @return a map with occupied hotseat position set to non-null value.
+     */
+    public static LongArrayMap<Object> removeBrokenHotseatItems(Context context) throws Exception {
+        GridSizeMigrationTask task = new GridSizeMigrationTask(context,
+                LauncherAppState.getInstance().getInvariantDeviceProfile(),
+                getValidPackages(context), Integer.MAX_VALUE, Integer.MAX_VALUE);
+
+        // Load all the valid entries
+        ArrayList<DbEntry> items = task.loadHotseatEntries();
+        // Delete any entry marked for deletion by above load.
+        task.applyOperations();
+        LongArrayMap<Object> positions = new LongArrayMap<>();
+        for (DbEntry item : items) {
+            positions.put(item.screenId, item);
+        }
+        return positions;
+    }
+
+    /**
+     * Task to run grid migration in multiple steps when the size difference is more than 1.
+     */
+    protected static class MultiStepMigrationTask {
+        private final HashSet<String> mValidPackages;
+        private final Context mContext;
+
+        public MultiStepMigrationTask(HashSet<String> validPackages, Context context) {
+            mValidPackages = validPackages;
+            mContext = context;
+        }
+
+        public boolean migrate(Point sourceSize, Point targetSize) throws Exception {
+            boolean dbChanged = false;
+            if (!targetSize.equals(sourceSize)) {
+                if (sourceSize.x < targetSize.x) {
+                    // Source is smaller that target, just expand the grid without actual migration.
+                    sourceSize.x = targetSize.x;
+                }
+                if (sourceSize.y < targetSize.y) {
+                    // Source is smaller that target, just expand the grid without actual migration.
+                    sourceSize.y = targetSize.y;
+                }
+
+                // Migrate the workspace grid, such that the points differ by max 1 in x and y
+                // each on every step.
+                while (!targetSize.equals(sourceSize)) {
+                    // Get the next size, such that the points differ by max 1 in x and y each
+                    Point nextSize = new Point(sourceSize);
+                    if (targetSize.x < nextSize.x) {
+                        nextSize.x--;
+                    }
+                    if (targetSize.y < nextSize.y) {
+                        nextSize.y--;
+                    }
+                    if (runStepTask(sourceSize, nextSize)) {
+                        dbChanged = true;
+                    }
+                    sourceSize.set(nextSize.x, nextSize.y);
+                }
+            }
+            return dbChanged;
+        }
+
+        protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
+            return new GridSizeMigrationTask(mContext,
+                    LauncherAppState.getInstance().getInvariantDeviceProfile(),
+                    mValidPackages, sourceSize, nextSize).migrateWorkspace();
+        }
+    }
 }
diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
index 30f228c..c86ba86 100644
--- a/src/com/android/launcher3/model/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/PackageItemInfo.java
@@ -20,8 +20,6 @@
 
 import com.android.launcher3.ItemInfo;
 
-import java.util.Arrays;
-
 /**
  * Represents a {@link Package} in the widget tray section.
  */
@@ -48,18 +46,12 @@
      */
     public String titleSectionName;
 
-    int flags = 0;
-
     PackageItemInfo(String packageName) {
         this.packageName = packageName;
     }
 
     @Override
-    public String toString() {
-        return "PackageItemInfo(title=" + title + " id=" + this.id
-                + " type=" + this.itemType + " container=" + this.container
-                + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
-                + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
-                + " user=" + user + ")";
+    protected String dumpProperties() {
+        return super.dumpProperties() + " packageName=" + packageName;
     }
 }
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
new file mode 100644
index 0000000..0d7ba1e
--- /dev/null
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -0,0 +1,84 @@
+package com.android.launcher3.model;
+
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.ComponentKey;
+
+import java.text.Collator;
+
+/**
+ * An wrapper over various items displayed in a widget picker,
+ * {@link LauncherAppWidgetProviderInfo} & {@link ActivityInfo}. This provides easier access to
+ * common attributes like spanX and spanY.
+ */
+public class WidgetItem extends ComponentKey implements Comparable<WidgetItem> {
+
+    private static UserHandleCompat sMyUserHandle;
+    private static Collator sCollator;
+
+    public final LauncherAppWidgetProviderInfo widgetInfo;
+    public final ActivityInfo activityInfo;
+
+    public final String label;
+    public final int spanX, spanY;
+
+    public WidgetItem(LauncherAppWidgetProviderInfo info, AppWidgetManagerCompat widgetManager) {
+        super(info.provider, widgetManager.getUser(info));
+
+        label = Utilities.trim(widgetManager.loadLabel(info));
+        widgetInfo = info;
+        activityInfo = null;
+
+        InvariantDeviceProfile idv = LauncherAppState.getInstance().getInvariantDeviceProfile();
+        spanX = Math.min(info.spanX, idv.numColumns);
+        spanY = Math.min(info.spanY, idv.numRows);
+    }
+
+    public WidgetItem(ResolveInfo info, PackageManager pm) {
+        super(new ComponentName(info.activityInfo.packageName, info.activityInfo.name),
+                UserHandleCompat.myUserHandle());
+        label = Utilities.trim(info.loadLabel(pm));
+        widgetInfo = null;
+        activityInfo = info.activityInfo;
+        spanX = spanY = 1;
+    }
+
+    @Override
+    public int compareTo(WidgetItem another) {
+        if (sMyUserHandle == null) {
+            // Delay these object creation until required.
+            sMyUserHandle = UserHandleCompat.myUserHandle();
+            sCollator = Collator.getInstance();
+        }
+
+        // Independent of how the labels compare, if only one of the two widget info belongs to
+        // work profile, put that one in the back.
+        boolean thisWorkProfile = !sMyUserHandle.equals(user);
+        boolean otherWorkProfile = !sMyUserHandle.equals(another.user);
+        if (thisWorkProfile ^ otherWorkProfile) {
+            return thisWorkProfile ? 1 : -1;
+        }
+
+        int labelCompare = sCollator.compare(label, another.label);
+        if (labelCompare != 0) {
+            return labelCompare;
+        }
+
+        // If the label is same, put the smaller widget before the larger widget. If the area is
+        // also same, put the widget with smaller height before.
+        int thisArea = spanX * spanY;
+        int otherArea = another.spanX * another.spanY;
+        return thisArea == otherArea
+                ? Integer.compare(spanY, another.spanY)
+                : Integer.compare(thisArea, otherArea);
+    }
+}
diff --git a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
deleted file mode 100644
index b990560..0000000
--- a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package com.android.launcher3.model;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.UserHandleCompat;
-import com.android.launcher3.util.ComponentKey;
-
-import java.text.Collator;
-import java.util.Comparator;
-import java.util.HashMap;
-
-public class WidgetsAndShortcutNameComparator implements Comparator<Object> {
-    private final AppWidgetManagerCompat mManager;
-    private final PackageManager mPackageManager;
-    private final HashMap<ComponentKey, String> mLabelCache;
-    private final Collator mCollator;
-    private final UserHandleCompat mMainHandle;
-
-    public WidgetsAndShortcutNameComparator(Context context) {
-        mManager = AppWidgetManagerCompat.getInstance(context);
-        mPackageManager = context.getPackageManager();
-        mLabelCache = new HashMap<>();
-        mCollator = Collator.getInstance();
-        mMainHandle = UserHandleCompat.myUserHandle();
-    }
-
-    /**
-     * Resets any stored state.
-     */
-    public void reset() {
-        mLabelCache.clear();
-    }
-
-    @Override
-    public final int compare(Object objA, Object objB) {
-        ComponentKey keyA = getComponentKey(objA);
-        ComponentKey keyB = getComponentKey(objB);
-
-        // Independent of how the labels compare, if only one of the two widget info belongs to
-        // work profile, put that one in the back.
-        boolean aWorkProfile = !mMainHandle.equals(keyA.user);
-        boolean bWorkProfile = !mMainHandle.equals(keyB.user);
-        if (aWorkProfile && !bWorkProfile) {
-            return 1;
-        }
-        if (!aWorkProfile && bWorkProfile) {
-            return -1;
-        }
-
-        // Get the labels for comparison
-        String labelA = mLabelCache.get(keyA);
-        String labelB = mLabelCache.get(keyB);
-        if (labelA == null) {
-            labelA = getLabel(objA);
-            mLabelCache.put(keyA, labelA);
-        }
-        if (labelB == null) {
-            labelB = getLabel(objB);
-            mLabelCache.put(keyB, labelB);
-        }
-        return mCollator.compare(labelA, labelB);
-    }
-
-    /**
-     * @return a component key for the given widget or shortcut info.
-     */
-    private ComponentKey getComponentKey(Object o) {
-        if (o instanceof LauncherAppWidgetProviderInfo) {
-            LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
-            return new ComponentKey(widgetInfo.provider, mManager.getUser(widgetInfo));
-        } else {
-            ResolveInfo shortcutInfo = (ResolveInfo) o;
-            ComponentName cn = new ComponentName(shortcutInfo.activityInfo.packageName,
-                    shortcutInfo.activityInfo.name);
-            // Currently, there are no work profile shortcuts
-            return new ComponentKey(cn, UserHandleCompat.myUserHandle());
-        }
-    }
-
-    /**
-     * @return the label for the given widget or shortcut info.  This may be an expensive call.
-     */
-    private String getLabel(Object o) {
-        if (o instanceof LauncherAppWidgetProviderInfo) {
-            LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
-            return Utilities.trim(mManager.loadLabel(widgetInfo));
-        } else {
-            ResolveInfo shortcutInfo = (ResolveInfo) o;
-            return Utilities.trim(shortcutInfo.loadLabel(mPackageManager));
-        }
-    }
-};
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 53e5213..b2a94bb 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -2,9 +2,9 @@
 package com.android.launcher3.model;
 
 import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.DeadObjectException;
 import android.os.TransactionTooLargeException;
@@ -16,11 +16,10 @@
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
-import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -42,20 +41,18 @@
     private final ArrayList<PackageItemInfo> mPackageItemInfos;
 
     /* Map of widgets and shortcuts that are tracked per package. */
-    private final HashMap<PackageItemInfo, ArrayList<Object>> mWidgetsList;
+    private final HashMap<PackageItemInfo, ArrayList<WidgetItem>> mWidgetsList;
 
     private final AppWidgetManagerCompat mAppWidgetMgr;
-    private final WidgetsAndShortcutNameComparator mWidgetAndShortcutNameComparator;
     private final Comparator<ItemInfo> mAppNameComparator;
     private final IconCache mIconCache;
     private final AppFilter mAppFilter;
     private final AlphabeticIndexCompat mIndexer;
 
-    private ArrayList<Object> mRawList;
+    private ArrayList<WidgetItem> mRawList;
 
     public WidgetsModel(Context context,  IconCache iconCache, AppFilter appFilter) {
         mAppWidgetMgr = AppWidgetManagerCompat.getInstance(context);
-        mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context);
         mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator();
         mIconCache = iconCache;
         mAppFilter = appFilter;
@@ -70,13 +67,12 @@
     private WidgetsModel(WidgetsModel model) {
         mAppWidgetMgr = model.mAppWidgetMgr;
         mPackageItemInfos = (ArrayList<PackageItemInfo>) model.mPackageItemInfos.clone();
-        mWidgetsList = (HashMap<PackageItemInfo, ArrayList<Object>>) model.mWidgetsList.clone();
-        mWidgetAndShortcutNameComparator = model.mWidgetAndShortcutNameComparator;
+        mWidgetsList = (HashMap<PackageItemInfo, ArrayList<WidgetItem>>) model.mWidgetsList.clone();
         mAppNameComparator = model.mAppNameComparator;
         mIconCache = model.mIconCache;
         mAppFilter = model.mAppFilter;
         mIndexer = model.mIndexer;
-        mRawList = (ArrayList<Object>) model.mRawList.clone();
+        mRawList = (ArrayList<WidgetItem>) model.mRawList.clone();
     }
 
     // Access methods that may be deleted if the private fields are made package-private.
@@ -92,11 +88,11 @@
         return mPackageItemInfos.get(pos);
     }
 
-    public List<Object> getSortedWidgets(int pos) {
+    public List<WidgetItem> getSortedWidgets(int pos) {
         return mWidgetsList.get(mPackageItemInfos.get(pos));
     }
 
-    public ArrayList<Object> getRawList() {
+    public ArrayList<WidgetItem> getRawList() {
         return mRawList;
     }
 
@@ -105,22 +101,27 @@
     }
 
     public WidgetsModel updateAndClone(Context context) {
-        Utilities.assertWorkerThread();
+        Preconditions.assertWorkerThread();
 
         try {
-            final ArrayList<Object> widgetsAndShortcuts = new ArrayList<>();
+            final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
             // Widgets
-            for (AppWidgetProviderInfo widgetInfo :
-                    AppWidgetManagerCompat.getInstance(context).getAllProviders()) {
-                widgetsAndShortcuts.add(LauncherAppWidgetProviderInfo
-                        .fromProviderInfo(context, widgetInfo));
+            AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(context);
+            for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders()) {
+                widgetsAndShortcuts.add(new WidgetItem(
+                        LauncherAppWidgetProviderInfo.fromProviderInfo(context, widgetInfo),
+                        widgetManager));
             }
+
             // Shortcuts
-            widgetsAndShortcuts.addAll(context.getPackageManager().queryIntentActivities(
-                    new Intent(Intent.ACTION_CREATE_SHORTCUT), 0));
+            PackageManager pm = context.getPackageManager();
+            for (ResolveInfo info :
+                    pm.queryIntentActivities(new Intent(Intent.ACTION_CREATE_SHORTCUT), 0)) {
+                widgetsAndShortcuts.add(new WidgetItem(info, pm));
+            }
             setWidgetsAndShortcuts(widgetsAndShortcuts);
         } catch (Exception e) {
-            if (!LauncherAppState.isDogfoodBuild() &&
+            if (!ProviderConfig.IS_DOGFOOD_BUILD &&
                     (e.getCause() instanceof TransactionTooLargeException ||
                             e.getCause() instanceof DeadObjectException)) {
                 // the returned value may be incomplete and will not be refreshed until the next
@@ -134,7 +135,7 @@
         return clone();
     }
 
-    private void setWidgetsAndShortcuts(ArrayList<Object> rawWidgetsShortcuts) {
+    private void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts) {
         mRawList = rawWidgetsShortcuts;
         if (DEBUG) {
             Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
@@ -147,76 +148,63 @@
         // clear the lists.
         mWidgetsList.clear();
         mPackageItemInfos.clear();
-        mWidgetAndShortcutNameComparator.reset();
 
         InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
 
         // add and update.
-        for (Object o: rawWidgetsShortcuts) {
-            String packageName = "";
-            UserHandleCompat userHandle = null;
-            ComponentName componentName = null;
-            if (o instanceof LauncherAppWidgetProviderInfo) {
-                LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
-
+        for (WidgetItem item: rawWidgetsShortcuts) {
+            if (item.widgetInfo != null) {
                 // Ensure that all widgets we show can be added on a workspace of this size
-                int minSpanX = Math.min(widgetInfo.spanX, widgetInfo.minSpanX);
-                int minSpanY = Math.min(widgetInfo.spanY, widgetInfo.minSpanY);
-                if (minSpanX <= (int) idp.numColumns &&
-                    minSpanY <= (int) idp.numRows) {
-                    componentName = widgetInfo.provider;
-                    packageName = widgetInfo.provider.getPackageName();
-                    userHandle = mAppWidgetMgr.getUser(widgetInfo);
-                } else {
+                int minSpanX = Math.min(item.widgetInfo.spanX, item.widgetInfo.minSpanX);
+                int minSpanY = Math.min(item.widgetInfo.spanY, item.widgetInfo.minSpanY);
+                if (minSpanX > idp.numColumns || minSpanY > idp.numRows) {
                     if (DEBUG) {
                         Log.d(TAG, String.format(
                                 "Widget %s : (%d X %d) can't fit on this device",
-                                widgetInfo.provider, minSpanX, minSpanY));
+                                item.componentName, minSpanX, minSpanY));
                     }
                     continue;
                 }
-            } else if (o instanceof ResolveInfo) {
-                ResolveInfo resolveInfo = (ResolveInfo) o;
-                componentName = new ComponentName(resolveInfo.activityInfo.packageName,
-                        resolveInfo.activityInfo.name);
-                packageName = resolveInfo.activityInfo.packageName;
-                userHandle = UserHandleCompat.myUserHandle();
             }
 
-            if (componentName == null || userHandle == null) {
-                Log.e(TAG, String.format("Widget cannot be set for %s.", o.getClass().toString()));
-                continue;
-            }
-            if (mAppFilter != null && !mAppFilter.shouldShowApp(componentName)) {
+            if (mAppFilter != null && !mAppFilter.shouldShowApp(item.componentName)) {
                 if (DEBUG) {
                     Log.d(TAG, String.format("%s is filtered and not added to the widget tray.",
-                        packageName));
+                            item.componentName));
                 }
                 continue;
             }
 
+            String packageName = item.componentName.getPackageName();
             PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName);
-            ArrayList<Object> widgetsShortcutsList = mWidgetsList.get(pInfo);
-            if (widgetsShortcutsList != null) {
-                widgetsShortcutsList.add(o);
-            } else {
+            ArrayList<WidgetItem> widgetsShortcutsList = mWidgetsList.get(pInfo);
+
+            if (widgetsShortcutsList == null) {
                 widgetsShortcutsList = new ArrayList<>();
-                widgetsShortcutsList.add(o);
+
                 pInfo = new PackageItemInfo(packageName);
-                mIconCache.getTitleAndIconForApp(packageName, userHandle,
-                        true /* userLowResIcon */, pInfo);
-                pInfo.titleSectionName = mIndexer.computeSectionName(pInfo.title);
-                mWidgetsList.put(pInfo, widgetsShortcutsList);
                 tmpPackageItemInfos.put(packageName,  pInfo);
+
                 mPackageItemInfos.add(pInfo);
+                mWidgetsList.put(pInfo, widgetsShortcutsList);
             }
+
+            widgetsShortcutsList.add(item);
         }
 
-        // sort.
-        Collections.sort(mPackageItemInfos, mAppNameComparator);
-        for (PackageItemInfo p: mPackageItemInfos) {
-            Collections.sort(mWidgetsList.get(p), mWidgetAndShortcutNameComparator);
+        // Update each package entry
+        for (PackageItemInfo p : mPackageItemInfos) {
+            ArrayList<WidgetItem> widgetsShortcutsList = mWidgetsList.get(p);
+            Collections.sort(widgetsShortcutsList);
+
+            // Update the package entry based on the first item.
+            p.user = widgetsShortcutsList.get(0).user;
+            mIconCache.getTitleAndIconForApp(p, true /* userLowResIcon */);
+            p.titleSectionName = mIndexer.computeSectionName(p.title);
         }
+
+        // sort the package entries.
+        Collections.sort(mPackageItemInfos, mAppNameComparator);
     }
 
     /**
diff --git a/src/com/android/launcher3/pageindicators/CaretDrawable.java b/src/com/android/launcher3/pageindicators/CaretDrawable.java
new file mode 100644
index 0000000..4789f69
--- /dev/null
+++ b/src/com/android/launcher3/pageindicators/CaretDrawable.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.pageindicators;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+
+import com.android.launcher3.R;
+
+import android.graphics.drawable.Drawable;
+
+public class CaretDrawable extends Drawable {
+    public static final float PROGRESS_CARET_POINTING_UP = -1f;
+    public static final float PROGRESS_CARET_POINTING_DOWN = 1f;
+    public static final float PROGRESS_CARET_NEUTRAL = 0;
+
+    private float mCaretProgress = PROGRESS_CARET_NEUTRAL;
+
+    private Paint mShadowPaint = new Paint();
+    private Paint mCaretPaint = new Paint();
+    private Path mPath = new Path();
+    private final int mCaretSizePx;
+
+    public CaretDrawable(Context context) {
+        final Resources res = context.getResources();
+
+        final int strokeWidth = res.getDimensionPixelSize(R.dimen.all_apps_caret_stroke_width);
+        final int shadowSpread = res.getDimensionPixelSize(R.dimen.all_apps_caret_shadow_spread);
+
+        mCaretPaint.setColor(res.getColor(R.color.all_apps_caret_color));
+        mCaretPaint.setAntiAlias(true);
+        mCaretPaint.setStrokeWidth(strokeWidth);
+        mCaretPaint.setStyle(Paint.Style.STROKE);
+        mCaretPaint.setStrokeCap(Paint.Cap.SQUARE);
+        mCaretPaint.setStrokeJoin(Paint.Join.MITER);
+
+        mShadowPaint.setColor(res.getColor(R.color.all_apps_caret_shadow_color));
+        mShadowPaint.setAntiAlias(true);
+        mShadowPaint.setStrokeWidth(strokeWidth + (shadowSpread * 2));
+        mShadowPaint.setStyle(Paint.Style.STROKE);
+        mShadowPaint.setStrokeCap(Paint.Cap.ROUND);
+        mShadowPaint.setStrokeJoin(Paint.Join.ROUND);
+
+        mCaretSizePx = res.getDimensionPixelSize(R.dimen.all_apps_caret_size);
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mCaretSizePx;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mCaretSizePx;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        // Assumes caret paint is more important than shadow paint
+        if (Float.compare(mCaretPaint.getAlpha(), 0f) == 0) {
+            return;
+        }
+
+        // Assumes shadow stroke width is larger
+        final float width = getBounds().width() - mShadowPaint.getStrokeWidth();
+        final float height = getBounds().height() - mShadowPaint.getStrokeWidth();
+        final float left = getBounds().left + (mShadowPaint.getStrokeWidth() / 2);
+        final float top = getBounds().top + (mShadowPaint.getStrokeWidth() / 2);
+
+        // When the bounds are square, this will result in a caret with a right angle
+        final float verticalInset = (height / 4);
+        final float caretHeight = (height - (verticalInset * 2));
+
+        mPath.reset();
+        mPath.moveTo(left, top + caretHeight * (1 - getNormalizedCaretProgress()));
+        mPath.lineTo(left + (width / 2), top + caretHeight * getNormalizedCaretProgress());
+        mPath.lineTo(left + width, top + caretHeight * (1 - getNormalizedCaretProgress()));
+
+        canvas.drawPath(mPath, mShadowPaint);
+        canvas.drawPath(mPath, mCaretPaint);
+    }
+
+    /**
+     * Sets the caret progress
+     *
+     * @param progress The progress ({@value #PROGRESS_CARET_POINTING_UP} for pointing up,
+     * {@value #PROGRESS_CARET_POINTING_DOWN} for pointing down, {@value #PROGRESS_CARET_NEUTRAL}
+     * for neutral)
+     */
+    public void setCaretProgress(float progress) {
+        mCaretProgress = progress;
+        invalidateSelf();
+    }
+
+    /**
+     * Returns the caret progress
+     *
+     * @return The progress
+     */
+    public float getCaretProgress() {
+        return mCaretProgress;
+    }
+
+    /**
+     * Returns the caret progress normalized to [0..1]
+     *
+     * @return The normalized progress
+     */
+    public float getNormalizedCaretProgress() {
+        return (mCaretProgress - PROGRESS_CARET_POINTING_UP) /
+                (PROGRESS_CARET_POINTING_DOWN - PROGRESS_CARET_POINTING_UP);
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mCaretPaint.setAlpha(alpha);
+        mShadowPaint.setAlpha(alpha);
+        invalidateSelf();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+        // no-op
+    }
+}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicator.java b/src/com/android/launcher3/pageindicators/PageIndicator.java
new file mode 100644
index 0000000..47c2ffb
--- /dev/null
+++ b/src/com/android/launcher3/pageindicators/PageIndicator.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.pageindicators;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.dynamicui.ExtractedColors;
+
+/**
+ * Base class for a page indicator.
+ */
+public abstract class PageIndicator extends FrameLayout {
+    private CaretDrawable mCaretDrawable;
+
+    protected int mNumPages = 1;
+
+    public PageIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        setWillNotDraw(false);
+    }
+
+    public void setScroll(int currentScroll, int totalScroll) {}
+
+    public void setActiveMarker(int activePage) {}
+
+    public void addMarker() {
+        mNumPages++;
+        onPageCountChanged();
+    }
+
+    public void removeMarker() {
+        mNumPages--;
+        onPageCountChanged();
+    }
+
+    public void setMarkersCount(int numMarkers) {
+        mNumPages = numMarkers;
+        onPageCountChanged();
+    }
+
+    public CaretDrawable getCaretDrawable() {
+        return mCaretDrawable;
+    }
+
+    public void setCaretDrawable(CaretDrawable caretDrawable) {
+        if (mCaretDrawable != null) {
+            mCaretDrawable.setCallback(null);
+        }
+
+        mCaretDrawable = caretDrawable;
+
+        if (mCaretDrawable != null) {
+            mCaretDrawable.setCallback(this);
+        }
+    }
+
+    protected void onPageCountChanged() {}
+
+    public void setShouldAutoHide(boolean shouldAutoHide) {}
+
+    public void updateColor(ExtractedColors extractedColors) {}
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return super.verifyDrawable(who) || who == getCaretDrawable();
+    }
+}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java b/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
new file mode 100644
index 0000000..aedf283
--- /dev/null
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorCaretLandscape.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.pageindicators;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+
+/**
+ * Simply draws the caret drawable bottom-right aligned in the view. This ensures that we can have
+ * a view with as large an area as we want (for touching) while maintaining a caret of size
+ * all_apps_caret_size.  Used only for the landscape layout.
+ */
+public class PageIndicatorCaretLandscape extends PageIndicator {
+    // all apps pull up handle drawable.
+
+    public PageIndicatorCaretLandscape(Context context) {
+        this(context, null);
+    }
+
+    public PageIndicatorCaretLandscape(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public PageIndicatorCaretLandscape(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        int caretSize = context.getResources().getDimensionPixelSize(R.dimen.all_apps_caret_size);
+        CaretDrawable caretDrawable = new CaretDrawable(context);
+        caretDrawable.setBounds(0, 0, caretSize, caretSize);
+        setCaretDrawable(caretDrawable);
+
+        Launcher l = Launcher.getLauncher(context);
+        setOnTouchListener(l.getHapticFeedbackTouchListener());
+        setOnClickListener(l);
+        setOnLongClickListener(l);
+        setOnFocusChangeListener(l.mFocusHandler);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        Rect drawableBounds = getCaretDrawable().getBounds();
+        int count = canvas.save();
+        canvas.translate(getWidth() - drawableBounds.width(),
+                getHeight() - drawableBounds.height());
+        getCaretDrawable().draw(canvas);
+        canvas.restoreToCount(count);
+    }
+}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
new file mode 100644
index 0000000..fb9d2f7
--- /dev/null
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.pageindicators;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.Property;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.view.animation.Interpolator;
+import android.view.animation.OvershootInterpolator;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
+/**
+ * {@link PageIndicator} which shows dots per page. The active page is shown with the current
+ * accent color.
+ */
+public class PageIndicatorDots extends PageIndicator {
+
+    private static final float SHIFT_PER_ANIMATION = 0.5f;
+    private static final float SHIFT_THRESHOLD = 0.1f;
+    private static final long ANIMATION_DURATION = 150;
+
+    private static final int ENTER_ANIMATION_START_DELAY = 300;
+    private static final int ENTER_ANIMATION_STAGGERED_DELAY = 150;
+    private static final int ENTER_ANIMATION_DURATION = 400;
+
+    // This value approximately overshoots to 1.5 times the original size.
+    private static final float ENTER_ANIMATION_OVERSHOOT_TENSION = 4.9f;
+
+    private static final RectF sTempRect = new RectF();
+
+    private static final Property<PageIndicatorDots, Float> CURRENT_POSITION
+            = new Property<PageIndicatorDots, Float>(float.class, "current_position") {
+        @Override
+        public Float get(PageIndicatorDots obj) {
+            return obj.mCurrentPosition;
+        }
+
+        @Override
+        public void set(PageIndicatorDots obj, Float pos) {
+            obj.mCurrentPosition = pos;
+            obj.invalidate();
+            obj.invalidateOutline();
+        }
+    };
+
+    /**
+     * Listener for keep running the animation until the final state is reached.
+     */
+    private final AnimatorListenerAdapter mAnimCycleListener = new AnimatorListenerAdapter() {
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            mAnimator = null;
+            animateToPostion(mFinalPosition);
+        }
+    };
+
+    private final Paint mCirclePaint;
+    private final float mDotRadius;
+    private final int mActiveColor;
+    private final int mInActiveColor;
+    private final boolean mIsRtl;
+
+    private int mActivePage;
+
+    /**
+     * The current position of the active dot including the animation progress.
+     * For ex:
+     *   0.0  => Active dot is at position 0
+     *   0.33 => Active dot is at position 0 and is moving towards 1
+     *   0.50 => Active dot is at position [0, 1]
+     *   0.77 => Active dot has left position 0 and is collapsing towards position 1
+     *   1.0  => Active dot is at position 1
+     */
+    private float mCurrentPosition;
+    private float mFinalPosition;
+    private ObjectAnimator mAnimator;
+
+    private float[] mEntryAnimationRadiusFactors;
+
+    public PageIndicatorDots(Context context) {
+        this(context, null);
+    }
+
+    public PageIndicatorDots(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public PageIndicatorDots(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+        mCirclePaint.setStyle(Style.FILL);
+        mDotRadius = getResources().getDimension(R.dimen.page_indicator_dot_size) / 2;
+        setOutlineProvider(new MyOutlineProver());
+
+        mActiveColor = Utilities.getColorAccent(context);
+        mInActiveColor = getResources().getColor(R.color.page_indicator_dot_color);
+
+        mIsRtl = Utilities.isRtl(getResources());
+    }
+
+    @Override
+    public void setScroll(int currentScroll, int totalScroll) {
+        if (mNumPages > 1) {
+            if (mIsRtl) {
+                currentScroll = totalScroll - currentScroll;
+            }
+            int scrollPerPage = totalScroll / (mNumPages - 1);
+            int absScroll = mActivePage * scrollPerPage;
+            float scrollThreshold = SHIFT_THRESHOLD * scrollPerPage;
+
+            if ((absScroll - currentScroll) > scrollThreshold) {
+                // current scroll is before absolute scroll
+                animateToPostion(mActivePage - SHIFT_PER_ANIMATION);
+            } else if ((currentScroll - absScroll) > scrollThreshold) {
+                // current scroll is ahead of absolute scroll
+                animateToPostion(mActivePage + SHIFT_PER_ANIMATION);
+            } else {
+                animateToPostion(mActivePage);
+            }
+        }
+    }
+
+    private void animateToPostion(float position) {
+        mFinalPosition = position;
+        if (Math.abs(mCurrentPosition - mFinalPosition) < SHIFT_THRESHOLD) {
+            mCurrentPosition = mFinalPosition;
+        }
+        if (mAnimator == null && Float.compare(mCurrentPosition, mFinalPosition) != 0) {
+            float positionForThisAnim = mCurrentPosition > mFinalPosition ?
+                    mCurrentPosition - SHIFT_PER_ANIMATION : mCurrentPosition + SHIFT_PER_ANIMATION;
+            mAnimator = ObjectAnimator.ofFloat(this, CURRENT_POSITION, positionForThisAnim);
+            mAnimator.addListener(mAnimCycleListener);
+            mAnimator.setDuration(ANIMATION_DURATION);
+            mAnimator.start();
+        }
+    }
+
+    public void stopAllAnimations() {
+        if (mAnimator != null) {
+            mAnimator.removeAllListeners();
+            mAnimator.cancel();
+            mAnimator = null;
+        }
+        mFinalPosition = mActivePage;
+        CURRENT_POSITION.set(this, mFinalPosition);
+    }
+
+    /**
+     * Sets up up the page indicator to play the entry animation.
+     * {@link #playEntryAnimation()} must be called after this.
+     */
+    public void prepareEntryAnimation() {
+        mEntryAnimationRadiusFactors = new float[mNumPages];
+        invalidate();
+    }
+
+    public void playEntryAnimation() {
+        int count  = mEntryAnimationRadiusFactors.length;
+        if (count == 0) {
+            mEntryAnimationRadiusFactors = null;
+            invalidate();
+            return;
+        }
+
+        Interpolator interpolator = new OvershootInterpolator(ENTER_ANIMATION_OVERSHOOT_TENSION);
+        AnimatorSet animSet = new AnimatorSet();
+        for (int i = 0; i < count; i++) {
+            ValueAnimator anim = ValueAnimator.ofFloat(0, 1).setDuration(ENTER_ANIMATION_DURATION);
+            final int index = i;
+            anim.addUpdateListener(new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    mEntryAnimationRadiusFactors[index] = (Float) animation.getAnimatedValue();
+                    invalidate();
+                }
+            });
+            anim.setInterpolator(interpolator);
+            anim.setStartDelay(ENTER_ANIMATION_START_DELAY + ENTER_ANIMATION_STAGGERED_DELAY * i);
+            animSet.play(anim);
+        }
+
+        animSet.addListener(new AnimatorListenerAdapter() {
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mEntryAnimationRadiusFactors = null;
+                invalidateOutline();
+                invalidate();
+            }
+        });
+        animSet.start();
+    }
+
+    @Override
+    public void setActiveMarker(int activePage) {
+        if (mActivePage != activePage) {
+            mActivePage = activePage;
+        }
+    }
+
+    @Override
+    protected void onPageCountChanged() {
+        requestLayout();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // Add extra spacing of mDotRadius on all sides so than entry animation could be run.
+        int width = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY ?
+                MeasureSpec.getSize(widthMeasureSpec) : (int) ((mNumPages * 3 + 2) * mDotRadius);
+        int height= MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY ?
+                MeasureSpec.getSize(heightMeasureSpec) : (int) (4 * mDotRadius);
+        setMeasuredDimension(width, height);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        // Draw all page indicators;
+        float circleGap = 3 * mDotRadius;
+        float startX = (getWidth() - mNumPages * circleGap + mDotRadius) / 2;
+
+        float x = startX + mDotRadius;
+        float y = canvas.getHeight() / 2;
+
+        if (mEntryAnimationRadiusFactors != null) {
+            // During entry animation, only draw the circles
+            if (mIsRtl) {
+                x = getWidth() - x;
+                circleGap = -circleGap;
+            }
+            for (int i = 0; i < mEntryAnimationRadiusFactors.length; i++) {
+                mCirclePaint.setColor(i == mActivePage ? mActiveColor : mInActiveColor);
+                canvas.drawCircle(x, y, mDotRadius * mEntryAnimationRadiusFactors[i], mCirclePaint);
+                x += circleGap;
+            }
+        } else {
+            mCirclePaint.setColor(mInActiveColor);
+            for (int i = 0; i < mNumPages; i++) {
+                canvas.drawCircle(x, y, mDotRadius, mCirclePaint);
+                x += circleGap;
+            }
+
+            mCirclePaint.setColor(mActiveColor);
+            canvas.drawRoundRect(getActiveRect(), mDotRadius, mDotRadius, mCirclePaint);
+        }
+    }
+
+    private RectF getActiveRect() {
+        float startCircle = (int) mCurrentPosition;
+        float delta = mCurrentPosition - startCircle;
+        float diameter = 2 * mDotRadius;
+        float circleGap = 3 * mDotRadius;
+        float startX = (getWidth() - mNumPages * circleGap + mDotRadius) / 2;
+
+        sTempRect.top = getHeight() * 0.5f - mDotRadius;
+        sTempRect.bottom = getHeight() * 0.5f + mDotRadius;
+        sTempRect.left = startX + startCircle * circleGap;
+        sTempRect.right = sTempRect.left + diameter;
+
+        if (delta < SHIFT_PER_ANIMATION) {
+            // dot is capturing the right circle.
+            sTempRect.right += delta * circleGap * 2;
+        } else {
+            // Dot is leaving the left circle.
+            sTempRect.right += circleGap;
+
+            delta -= SHIFT_PER_ANIMATION;
+            sTempRect.left += delta * circleGap * 2;
+        }
+
+        if (mIsRtl) {
+            float rectWidth = sTempRect.width();
+            sTempRect.right = getWidth() - sTempRect.left;
+            sTempRect.left = sTempRect.right - rectWidth;
+        }
+        return sTempRect;
+    }
+
+    private class MyOutlineProver extends ViewOutlineProvider {
+
+        @Override
+        public void getOutline(View view, Outline outline) {
+            if (mEntryAnimationRadiusFactors == null) {
+                RectF activeRect = getActiveRect();
+                outline.setRoundRect(
+                        (int) activeRect.left,
+                        (int) activeRect.top,
+                        (int) activeRect.right,
+                        (int) activeRect.bottom,
+                        mDotRadius
+                );
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
new file mode 100644
index 0000000..3ceba84
--- /dev/null
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorLineCaret.java
@@ -0,0 +1,281 @@
+package com.android.launcher3.pageindicators;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.v4.graphics.ColorUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Property;
+import android.view.ViewConfiguration;
+import android.widget.ImageView;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.dynamicui.ExtractedColors;
+
+/**
+ * A PageIndicator that briefly shows a fraction of a line when moving between pages.
+ *
+ * The fraction is 1 / number of pages and the position is based on the progress of the page scroll.
+ */
+public class PageIndicatorLineCaret extends PageIndicator {
+    private static final String TAG = "PageIndicatorLine";
+
+    private static final int[] sTempCoords = new int[2];
+
+    private static final int LINE_ANIMATE_DURATION = ViewConfiguration.getScrollBarFadeDuration();
+    private static final int LINE_FADE_DELAY = ViewConfiguration.getScrollDefaultDelay();
+    public static final int WHITE_ALPHA = (int) (0.70f * 255);
+    public static final int BLACK_ALPHA = (int) (0.65f * 255);
+
+    private static final int LINE_ALPHA_ANIMATOR_INDEX = 0;
+    private static final int NUM_PAGES_ANIMATOR_INDEX = 1;
+    private static final int TOTAL_SCROLL_ANIMATOR_INDEX = 2;
+
+    private ValueAnimator[] mAnimators = new ValueAnimator[3];
+
+    private final Handler mDelayedLineFadeHandler = new Handler(Looper.getMainLooper());
+
+    private boolean mShouldAutoHide = true;
+
+    // The alpha of the line when it is showing.
+    private int mActiveAlpha = 0;
+    // The alpha that the line is being animated to or already at (either 0 or mActiveAlpha).
+    private int mToAlpha;
+    // A float value representing the number of pages, to allow for an animation when it changes.
+    private float mNumPagesFloat;
+    private int mCurrentScroll;
+    private int mTotalScroll;
+    private Paint mLinePaint;
+    private Launcher mLauncher;
+    private final int mLineHeight;
+    private ImageView mAllAppsHandle;
+
+    private static final Property<PageIndicatorLineCaret, Integer> PAINT_ALPHA
+            = new Property<PageIndicatorLineCaret, Integer>(Integer.class, "paint_alpha") {
+        @Override
+        public Integer get(PageIndicatorLineCaret obj) {
+            return obj.mLinePaint.getAlpha();
+        }
+
+        @Override
+        public void set(PageIndicatorLineCaret obj, Integer alpha) {
+            obj.mLinePaint.setAlpha(alpha);
+            obj.invalidate();
+        }
+    };
+
+    private static final Property<PageIndicatorLineCaret, Float> NUM_PAGES
+            = new Property<PageIndicatorLineCaret, Float>(Float.class, "num_pages") {
+        @Override
+        public Float get(PageIndicatorLineCaret obj) {
+            return obj.mNumPagesFloat;
+        }
+
+        @Override
+        public void set(PageIndicatorLineCaret obj, Float numPages) {
+            obj.mNumPagesFloat = numPages;
+            obj.invalidate();
+        }
+    };
+
+    private static final Property<PageIndicatorLineCaret, Integer> TOTAL_SCROLL
+            = new Property<PageIndicatorLineCaret, Integer>(Integer.class, "total_scroll") {
+        @Override
+        public Integer get(PageIndicatorLineCaret obj) {
+            return obj.mTotalScroll;
+        }
+
+        @Override
+        public void set(PageIndicatorLineCaret obj, Integer totalScroll) {
+            obj.mTotalScroll = totalScroll;
+            obj.invalidate();
+        }
+    };
+
+    private Runnable mHideLineRunnable = new Runnable() {
+        @Override
+        public void run() {
+            animateLineToAlpha(0);
+        }
+    };
+
+    public PageIndicatorLineCaret(Context context) {
+        this(context, null);
+    }
+
+    public PageIndicatorLineCaret(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public PageIndicatorLineCaret(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        Resources res = context.getResources();
+        mLinePaint = new Paint();
+        mLinePaint.setAlpha(0);
+
+        mLauncher = Launcher.getLauncher(context);
+        mLineHeight = res.getDimensionPixelSize(R.dimen.dynamic_grid_page_indicator_line_height);
+        setCaretDrawable(new CaretDrawable(context));
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mAllAppsHandle = (ImageView) findViewById(R.id.all_apps_handle);
+        mAllAppsHandle.setImageDrawable(getCaretDrawable());
+        mAllAppsHandle.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
+        mAllAppsHandle.setOnClickListener(mLauncher);
+        mAllAppsHandle.setOnLongClickListener(mLauncher);
+        mAllAppsHandle.setOnFocusChangeListener(mLauncher.mFocusHandler);
+        mLauncher.setAllAppsButton(mAllAppsHandle);
+    }
+
+    @Override
+    public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
+        mAllAppsHandle.setAccessibilityDelegate(delegate);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mTotalScroll == 0 || mNumPagesFloat == 0) {
+            return;
+        }
+
+        // Compute and draw line rect.
+        float progress = Utilities.boundToRange(((float) mCurrentScroll) / mTotalScroll, 0f, 1f);
+        int availableWidth = canvas.getWidth();
+        int lineWidth = (int) (availableWidth / mNumPagesFloat);
+        int lineLeft = (int) (progress * (availableWidth - lineWidth));
+        int lineRight = lineLeft + lineWidth;
+        canvas.drawRect(lineLeft, canvas.getHeight() - mLineHeight, lineRight, canvas.getHeight(),
+                mLinePaint);
+    }
+
+    @Override
+    public void setContentDescription(CharSequence contentDescription) {
+        mAllAppsHandle.setContentDescription(contentDescription);
+    }
+
+    @Override
+    public void setScroll(int currentScroll, int totalScroll) {
+        if (getAlpha() == 0) {
+            return;
+        }
+        animateLineToAlpha(mActiveAlpha);
+
+        mCurrentScroll = currentScroll;
+        if (mTotalScroll == 0) {
+            mTotalScroll = totalScroll;
+        } else if (mTotalScroll != totalScroll) {
+            animateToTotalScroll(totalScroll);
+        } else {
+            invalidate();
+        }
+
+        if (mShouldAutoHide) {
+            hideAfterDelay();
+        }
+    }
+
+    private void hideAfterDelay() {
+        mDelayedLineFadeHandler.removeCallbacksAndMessages(null);
+        mDelayedLineFadeHandler.postDelayed(mHideLineRunnable, LINE_FADE_DELAY);
+    }
+
+    @Override
+    public void setActiveMarker(int activePage) {
+    }
+
+    @Override
+    protected void onPageCountChanged() {
+        if (Float.compare(mNumPages, mNumPagesFloat) != 0) {
+            animateToNumPages(mNumPages);
+        }
+    }
+
+    public void setShouldAutoHide(boolean shouldAutoHide) {
+        mShouldAutoHide = shouldAutoHide;
+        if (shouldAutoHide && mLinePaint.getAlpha() > 0) {
+            hideAfterDelay();
+        } else if (!shouldAutoHide) {
+            mDelayedLineFadeHandler.removeCallbacksAndMessages(null);
+        }
+    }
+
+    /**
+     * The line's color will be:
+     * - mostly opaque white if the hotseat is white (ignoring alpha)
+     * - mostly opaque black if the hotseat is black (ignoring alpha)
+     */
+    public void updateColor(ExtractedColors extractedColors) {
+        int originalLineAlpha = mLinePaint.getAlpha();
+        int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX, Color.TRANSPARENT);
+        if (color != Color.TRANSPARENT) {
+            color = ColorUtils.setAlphaComponent(color, 255);
+            if (color == Color.BLACK) {
+                mActiveAlpha = BLACK_ALPHA;
+            } else if (color == Color.WHITE) {
+                mActiveAlpha = WHITE_ALPHA;
+            } else {
+                Log.e(TAG, "Setting workspace page indicators to an unsupported color: #"
+                        + Integer.toHexString(color));
+            }
+            mLinePaint.setColor(color);
+            mLinePaint.setAlpha(originalLineAlpha);
+        }
+    }
+
+    private void animateLineToAlpha(int alpha) {
+        if (alpha == mToAlpha) {
+            // Ignore the new animation if it is going to the same alpha as the current animation.
+            return;
+        }
+        mToAlpha = alpha;
+        setupAndRunAnimation(ObjectAnimator.ofInt(this, PAINT_ALPHA, alpha),
+                LINE_ALPHA_ANIMATOR_INDEX);
+    }
+
+    private void animateToNumPages(int numPages) {
+        setupAndRunAnimation(ObjectAnimator.ofFloat(this, NUM_PAGES, numPages),
+                NUM_PAGES_ANIMATOR_INDEX);
+    }
+
+    private void animateToTotalScroll(int totalScroll) {
+        setupAndRunAnimation(ObjectAnimator.ofInt(this, TOTAL_SCROLL, totalScroll),
+                TOTAL_SCROLL_ANIMATOR_INDEX);
+    }
+
+    /**
+     * Starts the given animator and stores it in the provided index in {@link #mAnimators} until
+     * the animation ends.
+     *
+     * If an animator is already at the index (i.e. it is already playing), it is canceled and
+     * replaced with the new animator.
+     */
+    private void setupAndRunAnimation(ValueAnimator animator, final int animatorIndex) {
+        if (mAnimators[animatorIndex] != null) {
+            mAnimators[animatorIndex].cancel();
+        }
+        mAnimators[animatorIndex] = animator;
+        mAnimators[animatorIndex].addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimators[animatorIndex] = null;
+            }
+        });
+        mAnimators[animatorIndex].setDuration(LINE_ANIMATE_DURATION);
+        mAnimators[animatorIndex].start();
+    }
+}
diff --git a/src/com/android/launcher3/provider/ImportDataTask.java b/src/com/android/launcher3/provider/ImportDataTask.java
new file mode 100644
index 0000000..5cb34e8
--- /dev/null
+++ b/src/com/android/launcher3/provider/ImportDataTask.java
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.provider;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.Process;
+import android.text.TextUtils;
+import android.util.LongSparseArray;
+import android.util.SparseBooleanArray;
+
+import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
+import com.android.launcher3.DefaultLayoutParser;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherFiles;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherSettings.Settings;
+import com.android.launcher3.LauncherSettings.WorkspaceScreens;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.GridSizeMigrationTask;
+import com.android.launcher3.util.LongArrayMap;
+
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * Utility class to import data from another Launcher which is based on Launcher3 schema.
+ */
+public class ImportDataTask {
+
+    public static final String KEY_DATA_IMPORT_SRC_PKG = "data_import_src_pkg";
+    public static final String KEY_DATA_IMPORT_SRC_AUTHORITY = "data_import_src_authority";
+
+    private static final String TAG = "ImportDataTask";
+    private static final int MIN_ITEM_COUNT_FOR_SUCCESSFUL_MIGRATION = 6;
+    // Insert items progressively to avoid OOM exception when loading icons.
+    private static final int BATCH_INSERT_SIZE = 15;
+
+    private final Context mContext;
+
+    private final Uri mOtherScreensUri;
+    private final Uri mOtherFavoritesUri;
+
+    private int mHotseatSize;
+    private int mMaxGridSizeX;
+    private int mMaxGridSizeY;
+
+    private ImportDataTask(Context context, String sourceAuthority) {
+        mContext = context;
+        mOtherScreensUri = Uri.parse("content://" +
+                sourceAuthority + "/" + WorkspaceScreens.TABLE_NAME);
+        mOtherFavoritesUri = Uri.parse("content://" + sourceAuthority + "/" + Favorites.TABLE_NAME);
+    }
+
+    public boolean importWorkspace() throws Exception {
+        ArrayList<Long> allScreens = LauncherDbUtils.getScreenIdsFromCursor(
+                mContext.getContentResolver().query(mOtherScreensUri, null, null, null,
+                        LauncherSettings.WorkspaceScreens.SCREEN_RANK));
+
+        // During import we reset the screen IDs to 0-indexed values.
+        if (allScreens.isEmpty()) {
+            // No thing to migrate
+            return false;
+        }
+
+        mHotseatSize = mMaxGridSizeX = mMaxGridSizeY = 0;
+
+        // Build screen update
+        ArrayList<ContentProviderOperation> screenOps = new ArrayList<>();
+        int count = allScreens.size();
+        LongSparseArray<Long> screenIdMap = new LongSparseArray<>(count);
+        for (int i = 0; i < count; i++) {
+            ContentValues v = new ContentValues();
+            v.put(LauncherSettings.WorkspaceScreens._ID, i);
+            v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
+            screenIdMap.put(allScreens.get(i), (long) i);
+            screenOps.add(ContentProviderOperation.newInsert(
+                    LauncherSettings.WorkspaceScreens.CONTENT_URI).withValues(v).build());
+        }
+        mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY, screenOps);
+        importWorkspaceItems(allScreens.get(0), screenIdMap);
+
+        GridSizeMigrationTask.markForMigration(mContext, mMaxGridSizeX, mMaxGridSizeY, mHotseatSize);
+
+        // Create empty DB flag.
+        LauncherSettings.Settings.call(mContext.getContentResolver(),
+                LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
+        return true;
+    }
+
+    /**
+     * 1) Imports all the workspace entries from the source provider.
+     * 2) For home screen entries, maps the screen id based on {@param screenIdMap}
+     * 3) In the end fills any holes in hotseat with items from default hotseat layout.
+     */
+    private void importWorkspaceItems(
+            long firsetScreenId, LongSparseArray<Long> screenIdMap) throws Exception {
+        String profileId = Long.toString(UserManagerCompat.getInstance(mContext)
+                .getSerialNumberForUser(UserHandleCompat.myUserHandle()));
+
+        boolean createEmptyRowOnFirstScreen = false;
+        if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
+            try (Cursor c = mContext.getContentResolver().query(mOtherFavoritesUri, null,
+                    // get items on the first row of the first screen
+                    "profileId = ? AND container = -100 AND screen = ? AND cellY = 0",
+                    new String[]{profileId, Long.toString(firsetScreenId)},
+                    null)) {
+                // First row of first screen is not empty
+                createEmptyRowOnFirstScreen = c.moveToNext();
+            }
+        }
+
+        ArrayList<ContentProviderOperation> insertOperations = new ArrayList<>(BATCH_INSERT_SIZE);
+
+        // Set of package names present in hotseat
+        final HashSet<String> hotseatTargetApps = new HashSet<>();
+        int maxId = 0;
+
+        // Number of imported items on workspace and hotseat
+        int totalItemsOnWorkspace = 0;
+
+        try (Cursor c = mContext.getContentResolver()
+                .query(mOtherFavoritesUri, null,
+                        // Only migrate the primary user
+                        Favorites.PROFILE_ID + " = ?", new String[]{profileId},
+                        // Get the items sorted by container, so that the folders are loaded
+                        // before the corresponding items.
+                        Favorites.CONTAINER)) {
+
+            // various columns we expect to exist.
+            final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
+            final int intentIndex = c.getColumnIndexOrThrow(Favorites.INTENT);
+            final int titleIndex = c.getColumnIndexOrThrow(Favorites.TITLE);
+            final int containerIndex = c.getColumnIndexOrThrow(Favorites.CONTAINER);
+            final int itemTypeIndex = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
+            final int widgetProviderIndex = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER);
+            final int screenIndex = c.getColumnIndexOrThrow(Favorites.SCREEN);
+            final int cellXIndex = c.getColumnIndexOrThrow(Favorites.CELLX);
+            final int cellYIndex = c.getColumnIndexOrThrow(Favorites.CELLY);
+            final int spanXIndex = c.getColumnIndexOrThrow(Favorites.SPANX);
+            final int spanYIndex = c.getColumnIndexOrThrow(Favorites.SPANY);
+            final int rankIndex = c.getColumnIndexOrThrow(Favorites.RANK);
+            final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON);
+            final int iconPackageIndex = c.getColumnIndexOrThrow(Favorites.ICON_PACKAGE);
+            final int iconResourceIndex = c.getColumnIndexOrThrow(Favorites.ICON_RESOURCE);
+
+            SparseBooleanArray mValidFolders = new SparseBooleanArray();
+            ContentValues values = new ContentValues();
+
+            while (c.moveToNext()) {
+                values.clear();
+                int id = c.getInt(idIndex);
+                maxId = Math.max(maxId, id);
+                int type = c.getInt(itemTypeIndex);
+                int container = c.getInt(containerIndex);
+
+                long screen = c.getLong(screenIndex);
+
+                int cellX = c.getInt(cellXIndex);
+                int cellY = c.getInt(cellYIndex);
+                int spanX = c.getInt(spanXIndex);
+                int spanY = c.getInt(spanYIndex);
+
+                switch (container) {
+                    case Favorites.CONTAINER_DESKTOP: {
+                        Long newScreenId = screenIdMap.get(screen);
+                        if (newScreenId == null) {
+                            FileLog.d(TAG, String.format("Skipping item %d, type %d not on a valid screen %d", id, type, screen));
+                            continue;
+                        }
+                        // Reset the screen to 0-index value
+                        screen = newScreenId;
+                        if (createEmptyRowOnFirstScreen && screen == Workspace.FIRST_SCREEN_ID) {
+                            // Shift items by 1.
+                            cellY++;
+                        }
+
+                        mMaxGridSizeX = Math.max(mMaxGridSizeX, cellX + spanX);
+                        mMaxGridSizeY = Math.max(mMaxGridSizeY, cellY + spanY);
+                        break;
+                    }
+                    case Favorites.CONTAINER_HOTSEAT: {
+                        mHotseatSize = Math.max(mHotseatSize, (int) screen + 1);
+                        break;
+                    }
+                    default:
+                        if (!mValidFolders.get(container)) {
+                            FileLog.d(TAG, String.format("Skipping item %d, type %d not in a valid folder %d", id, type, container));
+                            continue;
+                        }
+                }
+
+                Intent intent = null;
+                switch (type) {
+                    case Favorites.ITEM_TYPE_FOLDER: {
+                        mValidFolders.put(id, true);
+                        // Use a empty intent to indicate a folder.
+                        intent = new Intent();
+                        break;
+                    }
+                    case Favorites.ITEM_TYPE_APPWIDGET: {
+                        values.put(Favorites.RESTORED,
+                                LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
+                                        LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
+                                        LauncherAppWidgetInfo.FLAG_UI_NOT_READY);
+                        values.put(Favorites.APPWIDGET_PROVIDER, c.getString(widgetProviderIndex));
+                        break;
+                    }
+                    case Favorites.ITEM_TYPE_SHORTCUT:
+                    case Favorites.ITEM_TYPE_APPLICATION: {
+                        intent = Intent.parseUri(c.getString(intentIndex), 0);
+                        if (Utilities.isLauncherAppTarget(intent)) {
+                            type = Favorites.ITEM_TYPE_APPLICATION;
+                        } else {
+                            values.put(Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
+                            values.put(Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
+                        }
+                        values.put(Favorites.ICON,  c.getBlob(iconIndex));
+                        values.put(Favorites.INTENT, intent.toUri(0));
+                        values.put(Favorites.RANK, c.getInt(rankIndex));
+
+                        values.put(Favorites.RESTORED, 1);
+                        break;
+                    }
+                    default:
+                        FileLog.d(TAG, String.format("Skipping item %d, not a valid type %d", id, type));
+                        continue;
+                }
+
+                if (container == Favorites.CONTAINER_HOTSEAT) {
+                    if (intent == null) {
+                        FileLog.d(TAG, String.format("Skipping item %d, null intent on hotseat", id));
+                        continue;
+                    }
+                    if (intent.getComponent() != null) {
+                        intent.setPackage(intent.getComponent().getPackageName());
+                    }
+                    hotseatTargetApps.add(getPackage(intent));
+                }
+
+                values.put(Favorites._ID, id);
+                values.put(Favorites.ITEM_TYPE, type);
+                values.put(Favorites.CONTAINER, container);
+                values.put(Favorites.SCREEN, screen);
+                values.put(Favorites.CELLX, cellX);
+                values.put(Favorites.CELLY, cellY);
+                values.put(Favorites.SPANX, spanX);
+                values.put(Favorites.SPANY, spanY);
+                values.put(Favorites.TITLE, c.getString(titleIndex));
+                insertOperations.add(ContentProviderOperation
+                        .newInsert(Favorites.CONTENT_URI).withValues(values).build());
+                if (container < 0) {
+                    totalItemsOnWorkspace++;
+                }
+
+                if (insertOperations.size() >= BATCH_INSERT_SIZE) {
+                    mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+                            insertOperations);
+                    insertOperations.clear();
+                }
+            }
+        }
+        if (totalItemsOnWorkspace < MIN_ITEM_COUNT_FOR_SUCCESSFUL_MIGRATION) {
+            throw new Exception("Insufficient data");
+        }
+        if (!insertOperations.isEmpty()) {
+            mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+                    insertOperations);
+            insertOperations.clear();
+        }
+
+        LongArrayMap<Object> hotseatItems = GridSizeMigrationTask.removeBrokenHotseatItems(mContext);
+        int myHotseatCount = LauncherAppState.getInstance().getInvariantDeviceProfile().numHotseatIcons;
+        if (!FeatureFlags.NO_ALL_APPS_ICON) {
+            myHotseatCount--;
+        }
+        if (hotseatItems.size() < myHotseatCount) {
+            // Insufficient hotseat items. Add a few more.
+            HotseatParserCallback parserCallback = new HotseatParserCallback(
+                    hotseatTargetApps, hotseatItems, insertOperations, maxId + 1, myHotseatCount);
+            new HotseatLayoutParser(mContext,
+                    parserCallback).loadLayout(null, new ArrayList<Long>());
+            mHotseatSize = (int) hotseatItems.keyAt(hotseatItems.size() - 1) + 1;
+
+            if (!insertOperations.isEmpty()) {
+                mContext.getContentResolver().applyBatch(ProviderConfig.AUTHORITY,
+                        insertOperations);
+            }
+        }
+    }
+
+    private static final String getPackage(Intent intent) {
+        return intent.getComponent() != null ? intent.getComponent().getPackageName()
+                : intent.getPackage();
+    }
+
+    /**
+     * Performs data import if possible.
+     * @return true on successful data import, false if it was not available
+     * @throws Exception if the import failed
+     */
+    public static boolean performImportIfPossible(Context context) throws Exception {
+        SharedPreferences devicePrefs = getDevicePrefs(context);
+        String sourcePackage = devicePrefs.getString(KEY_DATA_IMPORT_SRC_PKG, "");
+        String sourceAuthority = devicePrefs.getString(KEY_DATA_IMPORT_SRC_AUTHORITY, "");
+
+        if (TextUtils.isEmpty(sourcePackage) || TextUtils.isEmpty(sourceAuthority)) {
+            return false;
+        }
+
+        // Synchronously clear the migration flags. This ensures that we do not try migration
+        // again and thus prevents potential crash loops due to migration failure.
+        devicePrefs.edit().remove(KEY_DATA_IMPORT_SRC_PKG).remove(KEY_DATA_IMPORT_SRC_AUTHORITY).commit();
+
+        if (!Settings.call(context.getContentResolver(), Settings.METHOD_WAS_EMPTY_DB_CREATED)
+                .getBoolean(Settings.EXTRA_VALUE, false)) {
+            // Only migration if a new DB was created.
+            return false;
+        }
+
+        for (ProviderInfo info : context.getPackageManager().queryContentProviders(
+                null, context.getApplicationInfo().uid, 0)) {
+
+            if (sourcePackage.equals(info.packageName)) {
+                if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+                    // Only migrate if the source launcher is also on system image.
+                    return false;
+                }
+
+                // Wait until we found a provider with matching authority.
+                if (sourceAuthority.equals(info.authority)) {
+                    if (TextUtils.isEmpty(info.readPermission) ||
+                            context.checkPermission(info.readPermission, Process.myPid(),
+                                    Process.myUid()) == PackageManager.PERMISSION_GRANTED) {
+                        // All checks passed, run the import task.
+                        return new ImportDataTask(context, sourceAuthority).importWorkspace();
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private static SharedPreferences getDevicePrefs(Context c) {
+        return c.getSharedPreferences(LauncherFiles.DEVICE_PREFERENCES_KEY, Context.MODE_PRIVATE);
+    }
+
+    private static final int getMyHotseatLayoutId() {
+        return LauncherAppState.getInstance().getInvariantDeviceProfile().numHotseatIcons <= 5
+                ? R.xml.dw_phone_hotseat
+                : R.xml.dw_tablet_hotseat;
+    }
+
+    /**
+     * Extension of {@link DefaultLayoutParser} which only allows icons and shortcuts.
+     */
+    private static class HotseatLayoutParser extends DefaultLayoutParser {
+        public HotseatLayoutParser(Context context, LayoutParserCallback callback) {
+            super(context, null, callback, context.getResources(), getMyHotseatLayoutId());
+        }
+
+        @Override
+        protected HashMap<String, TagParser> getLayoutElementsMap() {
+            // Only allow shortcut parsers
+            HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+            parsers.put(TAG_FAVORITE, new AppShortcutWithUriParser());
+            parsers.put(TAG_SHORTCUT, new UriShortcutParser(mSourceRes));
+            parsers.put(TAG_RESOLVE, new ResolveParser());
+            return parsers;
+        }
+    }
+
+    /**
+     * {@link LayoutParserCallback} which adds items in empty hotseat spots.
+     */
+    private static class HotseatParserCallback implements LayoutParserCallback {
+        private final HashSet<String> mExisitingApps;
+        private final LongArrayMap<Object> mExistingItems;
+        private final ArrayList<ContentProviderOperation> mOutOps;
+        private final int mRequiredSize;
+        private int mStartItemId;
+
+        HotseatParserCallback(
+                HashSet<String> existingApps, LongArrayMap<Object> existingItems,
+                ArrayList<ContentProviderOperation> outOps, int startItemId, int requiredSize) {
+            mExisitingApps = existingApps;
+            mExistingItems = existingItems;
+            mOutOps = outOps;
+            mRequiredSize = requiredSize;
+            mStartItemId = startItemId;
+        }
+
+        @Override
+        public long generateNewItemId() {
+            return mStartItemId++;
+        }
+
+        @Override
+        public long insertAndCheck(SQLiteDatabase db, ContentValues values) {
+            if (mExistingItems.size() >= mRequiredSize) {
+                // No need to add more items.
+                return 0;
+            }
+            Intent intent;
+            try {
+                intent = Intent.parseUri(values.getAsString(Favorites.INTENT), 0);
+            } catch (URISyntaxException e) {
+                return 0;
+            }
+            String pkg = getPackage(intent);
+            if (pkg == null || mExisitingApps.contains(pkg)) {
+                // The item does not target an app or is already in hotseat.
+                return 0;
+            }
+            mExisitingApps.add(pkg);
+
+            // find next vacant spot.
+            long screen = 0;
+            while (mExistingItems.get(screen) != null) {
+                screen++;
+            }
+            mExistingItems.put(screen, intent);
+            values.put(Favorites.SCREEN, screen);
+            mOutOps.add(ContentProviderOperation.newInsert(Favorites.CONTENT_URI).withValues(values).build());
+            return 0;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
new file mode 100644
index 0000000..faa5fad
--- /dev/null
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.provider;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.util.Log;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherSettings.WorkspaceScreens;
+import com.android.launcher3.logging.FileLog;
+
+import java.util.ArrayList;
+
+/**
+ * A set of utility methods for Launcher DB used for DB updates and migration.
+ */
+public class LauncherDbUtils {
+
+    private static final String TAG = "LauncherDbUtils";
+
+    /**
+     * Makes the first screen as screen 0 (if screen 0 already exists,
+     * renames it to some other number).
+     * If the first row of screen 0 is non empty, runs a 'lossy' GridMigrationTask to clear
+     * the first row. The items in the first screen are moved and resized but the carry-forward
+     * items are simply deleted.
+     */
+    public static boolean prepareScreenZeroToHostQsb(SQLiteDatabase db) {
+        db.beginTransaction();
+        try {
+            // Get the existing screens
+            ArrayList<Long> screenIds = getScreenIdsFromCursor(db.query(WorkspaceScreens.TABLE_NAME,
+                    null, null, null, null, null, WorkspaceScreens.SCREEN_RANK));
+
+            if (screenIds.isEmpty()) {
+                // No update needed
+                return true;
+            }
+            if (screenIds.get(0) != 0) {
+                // First screen is not 0, we need to rename screens
+                if (screenIds.indexOf(0L) > -1) {
+                    // There is already a screen 0. First rename it to a differen screen.
+                    long newScreenId = 1;
+                    while (screenIds.indexOf(newScreenId) > -1) newScreenId++;
+                    renameScreen(db, 0, newScreenId);
+                }
+
+                // Rename the first screen to 0.
+                renameScreen(db, screenIds.get(0), 0);
+            }
+
+            // Check if the first row is empty
+            try (Cursor c = db.query(Favorites.TABLE_NAME, null,
+                    "container = -100 and screen = 0 and cellY = 0", null, null, null, null)) {
+                if (c.getCount() == 0) {
+                    // First row is empty, no need to migrate.
+                    return true;
+                }
+            }
+
+            LauncherAppState app = LauncherAppState.getInstance();
+            new LossyScreenMigrationTask(app.getContext(), app.getInvariantDeviceProfile(), db)
+                    .migrateScreen0();
+            db.setTransactionSuccessful();
+            return true;
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to update workspace size", e);
+            return false;
+        } finally {
+            db.endTransaction();
+        }
+    }
+
+    private static void renameScreen(SQLiteDatabase db, long oldScreen, long newScreen) {
+        String[] whereParams = new String[] { Long.toString(oldScreen) };
+
+        ContentValues values = new ContentValues();
+        values.put(WorkspaceScreens._ID, newScreen);
+        db.update(WorkspaceScreens.TABLE_NAME, values, "_id = ?", whereParams);
+
+        values.clear();
+        values.put(Favorites.SCREEN, newScreen);
+        db.update(Favorites.TABLE_NAME, values, "container = -100 and screen = ?", whereParams);
+    }
+
+    /**
+     * Parses the cursor containing workspace screens table and returns the list of screen IDs
+     */
+    public static ArrayList<Long> getScreenIdsFromCursor(Cursor sc) {
+        ArrayList<Long> screenIds = new ArrayList<Long>();
+        try {
+            final int idIndex = sc.getColumnIndexOrThrow(WorkspaceScreens._ID);
+            while (sc.moveToNext()) {
+                try {
+                    screenIds.add(sc.getLong(idIndex));
+                } catch (Exception e) {
+                    FileLog.d(TAG, "Invalid screen id", e);
+                }
+            }
+        } finally {
+            sc.close();
+        }
+        return screenIds;
+    }
+}
diff --git a/src/com/android/launcher3/provider/LossyScreenMigrationTask.java b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
new file mode 100644
index 0000000..4addbfa
--- /dev/null
+++ b/src/com/android/launcher3/provider/LossyScreenMigrationTask.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.provider;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.Point;
+import android.util.Log;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.model.GridSizeMigrationTask;
+import com.android.launcher3.util.LongArrayMap;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * An extension of {@link GridSizeMigrationTask} which migrates only one screen and
+ * deletes all carry-forward items.
+ */
+public class LossyScreenMigrationTask extends GridSizeMigrationTask {
+
+    private final SQLiteDatabase mDb;
+
+    private final LongArrayMap<DbEntry> mOriginalItems;
+    private final LongArrayMap<DbEntry> mUpdates;
+
+    protected LossyScreenMigrationTask(
+            Context context, InvariantDeviceProfile idp, SQLiteDatabase db) {
+        // Decrease the rows count by 1
+        super(context, idp, getValidPackages(context),
+                new Point(idp.numColumns, idp.numRows + 1),
+                new Point(idp.numColumns, idp.numRows));
+
+        mDb = db;
+        mOriginalItems = new LongArrayMap<>();
+        mUpdates = new LongArrayMap<>();
+    }
+
+    @Override
+    protected Cursor queryWorkspace(String[] columns, String where) {
+        return mDb.query(Favorites.TABLE_NAME, columns, where, null, null, null, null);
+    }
+
+    @Override
+    protected void update(DbEntry item) {
+        mUpdates.put(item.id, item.copy());
+    }
+
+    @Override
+    protected ArrayList<DbEntry> loadWorkspaceEntries(long screen) {
+        ArrayList<DbEntry> result = super.loadWorkspaceEntries(screen);
+        for (DbEntry entry : result) {
+            mOriginalItems.put(entry.id, entry.copy());
+
+            // Shift all items by 1 in y direction and mark them for update.
+            entry.cellY++;
+            mUpdates.put(entry.id, entry.copy());
+        }
+
+        return result;
+    }
+
+    public void migrateScreen0() {
+        migrateScreen(Workspace.FIRST_SCREEN_ID);
+
+        ContentValues tempValues = new ContentValues();
+        for (DbEntry update : mUpdates) {
+            DbEntry org = mOriginalItems.get(update.id);
+
+            if (org.cellX != update.cellX || org.cellY != update.cellY
+                    || org.spanX != update.spanX || org.spanY != update.spanY) {
+                tempValues.clear();
+                update.addToContentValues(tempValues);
+                mDb.update(Favorites.TABLE_NAME, tempValues, "_id = ?",
+                        new String[] {Long.toString(update.id)});
+            }
+        }
+
+        // Delete any carry over items as we are only migration a single screen.
+        for (DbEntry entry : mCarryOver) {
+            mEntryToRemove.add(entry.id);
+        }
+
+        if (!mEntryToRemove.isEmpty()) {
+            mDb.delete(Favorites.TABLE_NAME,
+                    Utilities.createDbSelectionQuery(Favorites._ID, mEntryToRemove), null);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
new file mode 100644
index 0000000..47bee06
--- /dev/null
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.provider;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherProvider.DatabaseHelper;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.logging.FileLog;
+
+import java.io.InvalidObjectException;
+
+/**
+ * Utility class to update DB schema after it has been restored.
+ *
+ * This task is executed when Launcher starts for the first time and not immediately after restore.
+ * This helps keep the model consistent if the launcher updates between restore and first startup.
+ */
+public class RestoreDbTask {
+
+    private static final String TAG = "RestoreDbTask";
+    private static final String RESTORE_TASK_PENDING = "restore_task_pending";
+
+    private static final String INFO_COLUMN_NAME = "name";
+    private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
+
+    /**
+     * When enabled all icons are kept on the home screen, even if they don't have an active
+     * session. To enable use:
+     *      adb shell setprop log.tag.launcher_keep_all_icons VERBOSE
+     */
+    private static final String KEEP_ALL_ICONS = "launcher_keep_all_icons";
+
+    public static boolean performRestore(DatabaseHelper helper) {
+        SQLiteDatabase db = helper.getWritableDatabase();
+        db.beginTransaction();
+        try {
+            new RestoreDbTask().sanitizeDB(helper, db);
+            db.setTransactionSuccessful();
+            return true;
+        } catch (Exception e) {
+            FileLog.e(TAG, "Failed to verify db", e);
+            return false;
+        } finally {
+            db.endTransaction();
+        }
+    }
+
+    /**
+     * Makes the following changes in the provider DB.
+     *   1. Removes all entries belonging to a managed profile as managed profiles
+     *      cannot be restored.
+     *   2. Marks all entries as restored. The flags are updated during first load or as
+     *      the restored apps get installed.
+     *   3. If the user serial for primary profile is different than that of the previous device,
+     *      update the entries to the new profile id.
+     */
+    private void sanitizeDB(DatabaseHelper helper, SQLiteDatabase db) throws Exception {
+        long oldProfileId = getDefaultProfileId(db);
+        // Delete all entries which do not belong to the main user
+        int itemsDeleted = db.delete(
+                Favorites.TABLE_NAME, "profileId != ?", new String[]{Long.toString(oldProfileId)});
+        if (itemsDeleted > 0) {
+            FileLog.d(TAG, itemsDeleted + " items belonging to a managed profile, were deleted");
+        }
+
+        // Mark all items as restored.
+        boolean keepAllIcons = Utilities.isPropertyEnabled(KEEP_ALL_ICONS);
+        ContentValues values = new ContentValues();
+        values.put(Favorites.RESTORED, ShortcutInfo.FLAG_RESTORED_ICON
+                | (keepAllIcons ? ShortcutInfo.FLAG_RESTORE_STARTED : 0));
+        db.update(Favorites.TABLE_NAME, values, null, null);
+
+        // Mark widgets with appropriate restore flag
+        values.put(Favorites.RESTORED,  LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
+                LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
+                LauncherAppWidgetInfo.FLAG_UI_NOT_READY |
+                (keepAllIcons ? LauncherAppWidgetInfo.FLAG_RESTORE_STARTED : 0));
+        db.update(Favorites.TABLE_NAME, values, "itemType = ?",
+                new String[]{Integer.toString(Favorites.ITEM_TYPE_APPWIDGET)});
+
+        long myProfileId = helper.getDefaultUserSerial();
+        if (Utilities.longCompare(oldProfileId, myProfileId) != 0) {
+            FileLog.d(TAG, "Changing primary user id from " + oldProfileId + " to " + myProfileId);
+            migrateProfileId(db, myProfileId);
+        }
+    }
+
+    /**
+     * Updates profile id of all entries and changes the default value for the column.
+     */
+    protected void migrateProfileId(SQLiteDatabase db, long newProfileId) {
+        // Update existing entries.
+        ContentValues values = new ContentValues();
+        values.put(Favorites.PROFILE_ID, newProfileId);
+        db.update(Favorites.TABLE_NAME, values, null, null);
+
+        // Change default value of the column.
+        db.execSQL("ALTER TABLE favorites RENAME TO favorites_old;");
+        Favorites.addTableToDb(db, newProfileId, false);
+        db.execSQL("INSERT INTO favorites SELECT * FROM favorites_old;");
+        db.execSQL("DROP TABLE favorites_old;");
+    }
+
+    /**
+     * Returns the profile id used in the favorites table of the provided db.
+     */
+    protected long getDefaultProfileId(SQLiteDatabase db) throws Exception {
+        try (Cursor c = db.rawQuery("PRAGMA table_info (favorites)", null)){
+            int nameIndex = c.getColumnIndex(INFO_COLUMN_NAME);
+            while (c.moveToNext()) {
+                if (Favorites.PROFILE_ID.equals(c.getString(nameIndex))) {
+                    return c.getLong(c.getColumnIndex(INFO_COLUMN_DEFAULT_VALUE));
+                }
+            }
+            throw new InvalidObjectException("Table does not have a profile id column");
+        }
+    }
+
+    public static boolean isPending(Context context) {
+        return Utilities.getPrefs(context).getBoolean(RESTORE_TASK_PENDING, false);
+    }
+
+    public static void setPending(Context context, boolean isPending) {
+        Utilities.getPrefs(context).edit().putBoolean(RESTORE_TASK_PENDING, isPending).commit();
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutManager.java b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
new file mode 100644
index 0000000..49d6fa9
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutManager.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.shortcuts;
+
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.LauncherApps;
+import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.UserHandleCompat;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Performs operations related to deep shortcuts, such as querying for them, pinning them, etc.
+ */
+public class DeepShortcutManager {
+    private static final String TAG = "DeepShortcutManager";
+
+    // TODO: Replace this with platform constants when the new sdk is available.
+    public static final int FLAG_MATCH_DYNAMIC = 1 << 0;
+    public static final int FLAG_MATCH_MANIFEST = 1 << 3;
+    public static final int FLAG_MATCH_PINNED = 1 << 1;
+
+    private static final int FLAG_GET_ALL =
+            FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST;
+
+    private final LauncherApps mLauncherApps;
+    private boolean mWasLastCallSuccess;
+
+    public DeepShortcutManager(Context context, ShortcutCache shortcutCache) {
+        mLauncherApps = (LauncherApps) context.getSystemService(Context.LAUNCHER_APPS_SERVICE);
+    }
+
+    public static boolean supportsShortcuts(ItemInfo info) {
+        return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+    }
+
+    public boolean wasLastCallSuccess() {
+        return mWasLastCallSuccess;
+    }
+
+    public void onShortcutsChanged(List<ShortcutInfoCompat> shortcuts) {
+        // mShortcutCache.removeShortcuts(shortcuts);
+    }
+
+    /**
+     * Queries for the shortcuts with the package name and provided ids.
+     *
+     * This method is intended to get the full details for shortcuts when they are added or updated,
+     * because we only get "key" fields in onShortcutsChanged().
+     */
+    public List<ShortcutInfoCompat> queryForFullDetails(String packageName,
+            List<String> shortcutIds, UserHandleCompat user) {
+        return query(FLAG_GET_ALL, packageName, null, shortcutIds, user);
+    }
+
+    /**
+     * Gets all the manifest and dynamic shortcuts associated with the given package and user,
+     * to be displayed in the shortcuts container on long press.
+     */
+    public List<ShortcutInfoCompat> queryForShortcutsContainer(ComponentName activity,
+            List<String> ids, UserHandleCompat user) {
+        return query(FLAG_MATCH_MANIFEST | FLAG_MATCH_DYNAMIC,
+                activity.getPackageName(), activity, ids, user);
+    }
+
+    /**
+     * Removes the given shortcut from the current list of pinned shortcuts.
+     * (Runs on background thread)
+     */
+    @TargetApi(25)
+    public void unpinShortcut(final ShortcutKey key) {
+        if (Utilities.isNycMR1OrAbove()) {
+            String packageName = key.componentName.getPackageName();
+            String id = key.getId();
+            UserHandleCompat user = key.user;
+            List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user));
+            pinnedIds.remove(id);
+            try {
+                mLauncherApps.pinShortcuts(packageName, pinnedIds, user.getUser());
+                mWasLastCallSuccess = true;
+            } catch (SecurityException|IllegalStateException e) {
+                Log.w(TAG, "Failed to unpin shortcut", e);
+                mWasLastCallSuccess = false;
+            }
+        }
+    }
+
+    /**
+     * Adds the given shortcut to the current list of pinned shortcuts.
+     * (Runs on background thread)
+     */
+    @TargetApi(25)
+    public void pinShortcut(final ShortcutKey key) {
+        if (Utilities.isNycMR1OrAbove()) {
+            String packageName = key.componentName.getPackageName();
+            String id = key.getId();
+            UserHandleCompat user = key.user;
+            List<String> pinnedIds = extractIds(queryForPinnedShortcuts(packageName, user));
+            pinnedIds.add(id);
+            try {
+                mLauncherApps.pinShortcuts(packageName, pinnedIds, user.getUser());
+                mWasLastCallSuccess = true;
+            } catch (SecurityException|IllegalStateException e) {
+                Log.w(TAG, "Failed to pin shortcut", e);
+                mWasLastCallSuccess = false;
+            }
+        }
+    }
+
+    @TargetApi(25)
+    public void startShortcut(String packageName, String id, Rect sourceBounds,
+          Bundle startActivityOptions, UserHandleCompat user) {
+        if (Utilities.isNycMR1OrAbove()) {
+            try {
+                mLauncherApps.startShortcut(packageName, id, sourceBounds,
+                        startActivityOptions, user.getUser());
+                mWasLastCallSuccess = true;
+            } catch (SecurityException|IllegalStateException e) {
+                Log.e(TAG, "Failed to start shortcut", e);
+                mWasLastCallSuccess = false;
+            }
+        }
+    }
+
+    @TargetApi(25)
+    public Drawable getShortcutIconDrawable(ShortcutInfoCompat shortcutInfo, int density) {
+        if (Utilities.isNycMR1OrAbove()) {
+            try {
+                Drawable icon = mLauncherApps.getShortcutIconDrawable(
+                        shortcutInfo.getShortcutInfo(), density);
+                mWasLastCallSuccess = true;
+                return icon;
+            } catch (SecurityException|IllegalStateException e) {
+                Log.e(TAG, "Failed to get shortcut icon", e);
+                mWasLastCallSuccess = false;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the id's of pinned shortcuts associated with the given package and user.
+     *
+     * If packageName is null, returns all pinned shortcuts regardless of package.
+     */
+    public List<ShortcutInfoCompat> queryForPinnedShortcuts(String packageName,
+            UserHandleCompat user) {
+        return query(FLAG_MATCH_PINNED, packageName, null, null, user);
+    }
+
+    public List<ShortcutInfoCompat> queryForAllShortcuts(UserHandleCompat user) {
+        return query(FLAG_GET_ALL, null, null, null, user);
+    }
+
+    private List<String> extractIds(List<ShortcutInfoCompat> shortcuts) {
+        List<String> shortcutIds = new ArrayList<>(shortcuts.size());
+        for (ShortcutInfoCompat shortcut : shortcuts) {
+            shortcutIds.add(shortcut.getId());
+        }
+        return shortcutIds;
+    }
+
+    /**
+     * Query the system server for all the shortcuts matching the given parameters.
+     * If packageName == null, we query for all shortcuts with the passed flags, regardless of app.
+     *
+     * TODO: Use the cache to optimize this so we don't make an RPC every time.
+     */
+    @TargetApi(25)
+    private List<ShortcutInfoCompat> query(int flags, String packageName,
+            ComponentName activity, List<String> shortcutIds, UserHandleCompat user) {
+        if (Utilities.isNycMR1OrAbove()) {
+            ShortcutQuery q = new ShortcutQuery();
+            q.setQueryFlags(flags);
+            if (packageName != null) {
+                q.setPackage(packageName);
+                q.setActivity(activity);
+                q.setShortcutIds(shortcutIds);
+            }
+            List<ShortcutInfo> shortcutInfos = null;
+            try {
+                shortcutInfos = mLauncherApps.getShortcuts(q, user.getUser());
+                mWasLastCallSuccess = true;
+            } catch (SecurityException|IllegalStateException e) {
+                Log.e(TAG, "Failed to query for shortcuts", e);
+                mWasLastCallSuccess = false;
+            }
+            if (shortcutInfos == null) {
+                return Collections.EMPTY_LIST;
+            }
+            List<ShortcutInfoCompat> shortcutInfoCompats = new ArrayList<>(shortcutInfos.size());
+            for (ShortcutInfo shortcutInfo : shortcutInfos) {
+                shortcutInfoCompats.add(new ShortcutInfoCompat(shortcutInfo));
+            }
+            return shortcutInfoCompats;
+        } else {
+            return Collections.EMPTY_LIST;
+        }
+    }
+
+    @TargetApi(25)
+    public boolean hasHostPermission() {
+        if (Utilities.isNycMR1OrAbove()) {
+            try {
+                return mLauncherApps.hasShortcutHostPermission();
+            } catch (SecurityException|IllegalStateException e) {
+                Log.e(TAG, "Failed to make shortcut manager call", e);
+            }
+        }
+        return false;
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
new file mode 100644
index 0000000..42086fc
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutTextView.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.shortcuts;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+
+/**
+ * A {@link BubbleTextView} that has the shortcut icon on the left and drag handle on the right.
+ */
+public class DeepShortcutTextView extends BubbleTextView {
+    private final Rect mDragHandleBounds = new Rect();
+    private final int mDragHandleWidth;
+    private boolean mShouldPerformClick = true;
+
+    public DeepShortcutTextView(Context context) {
+        this(context, null, 0);
+    }
+
+    public DeepShortcutTextView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public DeepShortcutTextView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        Resources resources = getResources();
+        mDragHandleWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcut_padding_end)
+                + resources.getDimensionPixelSize(R.dimen.deep_shortcut_drag_handle_size)
+                + resources.getDimensionPixelSize(R.dimen.deep_shortcut_drawable_padding) / 2;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        mDragHandleBounds.set(0, 0, mDragHandleWidth, getMeasuredHeight());
+        if (!Utilities.isRtl(getResources())) {
+            mDragHandleBounds.offset(getMeasuredWidth() - mDragHandleBounds.width(), 0);
+        }
+    }
+
+    @Override
+    protected void applyCompoundDrawables(Drawable icon) {
+        // The icon is drawn in a separate view.
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            // Ignore clicks on the drag handle (long clicks still start the drag).
+            mShouldPerformClick = !mDragHandleBounds.contains((int) ev.getX(), (int) ev.getY());
+        }
+        return super.onTouchEvent(ev);
+    }
+
+    @Override
+    public boolean performClick() {
+        return mShouldPerformClick && super.performClick();
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutView.java b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
new file mode 100644
index 0000000..e7fc415
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutView.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.shortcuts;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LogAccelerateInterpolator;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.shortcuts.DeepShortcutsContainer.UnbadgedShortcutInfo;
+import com.android.launcher3.util.PillRevealOutlineProvider;
+import com.android.launcher3.util.PillWidthRevealOutlineProvider;
+
+/**
+ * A {@link android.widget.FrameLayout} that contains a {@link DeepShortcutView}.
+ * This lets us animate the DeepShortcutView (icon and text) separately from the background.
+ */
+public class DeepShortcutView extends FrameLayout implements ValueAnimator.AnimatorUpdateListener {
+
+    private static final Point sTempPoint = new Point();
+
+    private final Rect mPillRect;
+
+    private DeepShortcutTextView mBubbleText;
+    private View mIconView;
+    private float mOpenAnimationProgress;
+
+    private UnbadgedShortcutInfo mInfo;
+
+    public DeepShortcutView(Context context) {
+        this(context, null, 0);
+    }
+
+    public DeepShortcutView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public DeepShortcutView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        mPillRect = new Rect();
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mIconView = findViewById(R.id.deep_shortcut_icon);
+        mBubbleText = (DeepShortcutTextView) findViewById(R.id.deep_shortcut);
+    }
+
+    public DeepShortcutTextView getBubbleText() {
+        return mBubbleText;
+    }
+
+    public void setWillDrawIcon(boolean willDraw) {
+        mIconView.setVisibility(willDraw ? View.VISIBLE : View.INVISIBLE);
+    }
+
+    public boolean willDrawIcon() {
+        return mIconView.getVisibility() == View.VISIBLE;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        mPillRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
+    }
+
+    /** package private **/
+    void applyShortcutInfo(UnbadgedShortcutInfo info, DeepShortcutsContainer container) {
+        mInfo = info;
+        IconCache cache = LauncherAppState.getInstance().getIconCache();
+        mBubbleText.applyFromShortcutInfo(info, cache);
+        mIconView.setBackground(mBubbleText.getIcon());
+
+        // Use the long label as long as it exists and fits.
+        CharSequence longLabel = info.mDetail.getLongLabel();
+        int availableWidth = mBubbleText.getWidth() - mBubbleText.getTotalPaddingLeft()
+                - mBubbleText.getTotalPaddingRight();
+        boolean usingLongLabel = !TextUtils.isEmpty(longLabel)
+                && mBubbleText.getPaint().measureText(longLabel.toString()) <= availableWidth;
+        mBubbleText.setText(usingLongLabel ? longLabel : info.mDetail.getShortLabel());
+
+        // TODO: Add the click handler to this view directly and not the child view.
+        mBubbleText.setOnClickListener(Launcher.getLauncher(getContext()));
+        mBubbleText.setOnLongClickListener(container);
+        mBubbleText.setOnTouchListener(container);
+    }
+
+    /**
+     * Returns the shortcut info that is suitable to be added on the homescreen
+     */
+    public ShortcutInfo getFinalInfo() {
+        ShortcutInfo badged = new ShortcutInfo(mInfo);
+        // Queue an update task on the worker thread. This ensures that the badged
+        // shortcut eventually gets its icon updated.
+        Launcher.getLauncher(getContext()).getModel().updateShortcutInfo(mInfo.mDetail, badged);
+        return badged;
+    }
+
+    public View getIconView() {
+        return mIconView;
+    }
+
+    /**
+     * Creates an animator to play when the shortcut container is being opened.
+     */
+    public Animator createOpenAnimation(boolean isContainerAboveIcon, boolean pivotLeft) {
+        Point center = getIconCenter();
+        ValueAnimator openAnimator =  new ZoomRevealOutlineProvider(center.x, center.y,
+                mPillRect, this, mIconView, isContainerAboveIcon, pivotLeft)
+                        .createRevealAnimator(this, false);
+        mOpenAnimationProgress = 0f;
+        openAnimator.addUpdateListener(this);
+        return openAnimator;
+    }
+
+    @Override
+    public void onAnimationUpdate(ValueAnimator valueAnimator) {
+        mOpenAnimationProgress = valueAnimator.getAnimatedFraction();
+    }
+
+    public boolean isOpenOrOpening() {
+        return mOpenAnimationProgress > 0;
+    }
+
+    /**
+     * Creates an animator to play when the shortcut container is being closed.
+     */
+    public Animator createCloseAnimation(boolean isContainerAboveIcon, boolean pivotLeft,
+            long duration) {
+        Point center = getIconCenter();
+        ValueAnimator closeAnimator =  new ZoomRevealOutlineProvider(center.x, center.y,
+                mPillRect, this, mIconView, isContainerAboveIcon, pivotLeft)
+                        .createRevealAnimator(this, true);
+        // Scale down the duration and interpolator according to the progress
+        // that the open animation was at when the close started.
+        closeAnimator.setDuration((long) (duration * mOpenAnimationProgress));
+        closeAnimator.setInterpolator(new CloseInterpolator(mOpenAnimationProgress));
+        return closeAnimator;
+    }
+
+    /**
+     * Creates an animator which clips the container to form a circle around the icon.
+     */
+    public Animator collapseToIcon() {
+        int halfHeight = getMeasuredHeight() / 2;
+        int iconCenterX = getIconCenter().x;
+        return new PillWidthRevealOutlineProvider(mPillRect,
+                iconCenterX - halfHeight, iconCenterX + halfHeight)
+                        .createRevealAnimator(this, true);
+    }
+
+    /**
+     * Returns the position of the center of the icon relative to the container.
+     */
+    public Point getIconCenter() {
+        sTempPoint.y = sTempPoint.x = getMeasuredHeight() / 2;
+        if (Utilities.isRtl(getResources())) {
+            sTempPoint.x = getMeasuredWidth() - sTempPoint.x;
+        }
+        return sTempPoint;
+    }
+
+    /**
+     * Extension of {@link PillRevealOutlineProvider} which scales the icon based on the height.
+     */
+    private static class ZoomRevealOutlineProvider extends PillRevealOutlineProvider {
+
+        private final View mTranslateView;
+        private final View mZoomView;
+
+        private final float mFullHeight;
+        private final float mTranslateYMultiplier;
+
+        private final boolean mPivotLeft;
+        private final float mTranslateX;
+
+        public ZoomRevealOutlineProvider(int x, int y, Rect pillRect,
+                View translateView, View zoomView, boolean isContainerAboveIcon, boolean pivotLeft) {
+            super(x, y, pillRect);
+            mTranslateView = translateView;
+            mZoomView = zoomView;
+            mFullHeight = pillRect.height();
+
+            mTranslateYMultiplier = isContainerAboveIcon ? 0.5f : -0.5f;
+
+            mPivotLeft = pivotLeft;
+            mTranslateX = pivotLeft ? pillRect.height() / 2 : pillRect.right - pillRect.height() / 2;
+        }
+
+        @Override
+        public void setProgress(float progress) {
+            super.setProgress(progress);
+
+            mZoomView.setScaleX(progress);
+            mZoomView.setScaleY(progress);
+
+            float height = mOutline.height();
+            mTranslateView.setTranslationY(mTranslateYMultiplier * (mFullHeight - height));
+
+            float pivotX = mPivotLeft ? (mOutline.left + height / 2) : (mOutline.right - height / 2);
+            mTranslateView.setTranslationX(mTranslateX - pivotX);
+        }
+    }
+
+    /**
+     * An interpolator that reverses the current open animation progress.
+     */
+    private static class CloseInterpolator extends LogAccelerateInterpolator {
+        private float mStartProgress;
+        private float mRemainingProgress;
+
+        /**
+         * @param openAnimationProgress The progress that the open interpolator ended at.
+         */
+        public CloseInterpolator(float openAnimationProgress) {
+            super(100, 0);
+            mStartProgress = 1f - openAnimationProgress;
+            mRemainingProgress = openAnimationProgress;
+        }
+
+        @Override
+        public float getInterpolation(float v) {
+            return mStartProgress + super.getInterpolation(v) * mRemainingProgress;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
new file mode 100644
index 0000000..2702d4e
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java
@@ -0,0 +1,675 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.shortcuts;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.ShapeDrawable;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.LinearLayout;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.IconCache;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherViewPropertyAnimator;
+import com.android.launcher3.LogAccelerateInterpolator;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.accessibility.ShortcutMenuAccessibilityDelegate;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
+import com.android.launcher3.graphics.TriangleShape;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A container for shortcuts to deep links within apps.
+ */
+@TargetApi(Build.VERSION_CODES.N)
+public class DeepShortcutsContainer extends LinearLayout implements View.OnLongClickListener,
+        View.OnTouchListener, DragSource, DragController.DragListener {
+    private static final String TAG = "ShortcutsContainer";
+
+    private final Point mIconShift = new Point();
+
+    private final Launcher mLauncher;
+    private final DeepShortcutManager mDeepShortcutsManager;
+    private final int mStartDragThreshold;
+    private final ShortcutMenuAccessibilityDelegate mAccessibilityDelegate;
+    private final boolean mIsRtl;
+
+    private BubbleTextView mDeferredDragIcon;
+    private final Rect mTempRect = new Rect();
+    private Point mIconLastTouchPos = new Point();
+    private boolean mIsLeftAligned;
+    private boolean mIsAboveIcon;
+    private View mArrow;
+
+    private Animator mOpenCloseAnimator;
+    private boolean mDeferContainerRemoval;
+    private boolean mIsOpen;
+
+    public DeepShortcutsContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mLauncher = Launcher.getLauncher(context);
+        mDeepShortcutsManager = LauncherAppState.getInstance().getShortcutManager();
+
+        mStartDragThreshold = getResources().getDimensionPixelSize(
+                R.dimen.deep_shortcuts_start_drag_threshold);
+        mAccessibilityDelegate = new ShortcutMenuAccessibilityDelegate(mLauncher);
+        mIsRtl = Utilities.isRtl(getResources());
+    }
+
+    public DeepShortcutsContainer(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public DeepShortcutsContainer(Context context) {
+        this(context, null, 0);
+    }
+
+    public void populateAndShow(final BubbleTextView originalIcon, final List<String> ids) {
+        final Resources resources = getResources();
+        final int arrowWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcuts_arrow_width);
+        final int arrowHeight = resources.getDimensionPixelSize(R.dimen.deep_shortcuts_arrow_height);
+        final int arrowHorizontalOffset = resources.getDimensionPixelSize(
+                R.dimen.deep_shortcuts_arrow_horizontal_offset);
+        final int arrowVerticalOffset = resources.getDimensionPixelSize(
+                R.dimen.deep_shortcuts_arrow_vertical_offset);
+
+        // Add dummy views first, and populate with real shortcut info when ready.
+        final int spacing = getResources().getDimensionPixelSize(R.dimen.deep_shortcuts_spacing);
+        final LayoutInflater inflater = mLauncher.getLayoutInflater();
+        int numShortcuts = Math.min(ids.size(), ShortcutFilter.MAX_SHORTCUTS);
+        for (int i = 0; i < numShortcuts; i++) {
+            final DeepShortcutView shortcut =
+                    (DeepShortcutView) inflater.inflate(R.layout.deep_shortcut, this, false);
+            if (i < numShortcuts - 1) {
+                ((LayoutParams) shortcut.getLayoutParams()).bottomMargin = spacing;
+            }
+            shortcut.getBubbleText().setAccessibilityDelegate(mAccessibilityDelegate);
+            addView(shortcut);
+        }
+        setContentDescription(getContext().getString(R.string.shortcuts_menu_description,
+                numShortcuts, originalIcon.getContentDescription().toString()));
+
+        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+        orientAboutIcon(originalIcon, arrowHeight + arrowVerticalOffset);
+
+        // Add the arrow.
+        mArrow = addArrowView(arrowHorizontalOffset, arrowVerticalOffset, arrowWidth, arrowHeight);
+        mArrow.setPivotX(arrowWidth / 2);
+        mArrow.setPivotY(mIsAboveIcon ? 0 : arrowHeight);
+
+        animateOpen();
+
+        deferDrag(originalIcon);
+
+        // Load the shortcuts on a background thread and update the container as it animates.
+        final Looper workerLooper = LauncherModel.getWorkerLooper();
+        final Handler uiHandler = new Handler(Looper.getMainLooper());
+        final ItemInfo originalInfo = (ItemInfo) originalIcon.getTag();
+        final UserHandleCompat user = originalInfo.user;
+        final ComponentName activity = originalInfo.getTargetComponent();
+        new Handler(workerLooper).postAtFrontOfQueue(new Runnable() {
+            @Override
+            public void run() {
+                final List<ShortcutInfoCompat> shortcuts = ShortcutFilter.sortAndFilterShortcuts(
+                        mDeepShortcutsManager.queryForShortcutsContainer(activity, ids, user));
+                // We want the lowest rank to be closest to the user's finger.
+                if (mIsAboveIcon) {
+                    Collections.reverse(shortcuts);
+                }
+                for (int i = 0; i < shortcuts.size(); i++) {
+                    final ShortcutInfoCompat shortcut = shortcuts.get(i);
+                    uiHandler.post(new UpdateShortcutChild(
+                            i, new UnbadgedShortcutInfo(shortcut, mLauncher)));
+                }
+            }
+        });
+    }
+
+    /** Updates the child of this container at the given index based on the given shortcut info. */
+    private class UpdateShortcutChild implements Runnable {
+        private int mShortcutChildIndex;
+        private UnbadgedShortcutInfo mShortcutChildInfo;
+
+        public UpdateShortcutChild(int shortcutChildIndex, UnbadgedShortcutInfo shortcutChildInfo) {
+            mShortcutChildIndex = shortcutChildIndex;
+            mShortcutChildInfo = shortcutChildInfo;
+        }
+
+        @Override
+        public void run() {
+            getShortcutAt(mShortcutChildIndex)
+                    .applyShortcutInfo(mShortcutChildInfo, DeepShortcutsContainer.this);
+        }
+    }
+
+    private DeepShortcutView getShortcutAt(int index) {
+        if (!mIsAboveIcon) {
+            // Opening down, so arrow is the first view.
+            index++;
+        }
+        return (DeepShortcutView) getChildAt(index);
+    }
+
+    private int getShortcutCount() {
+        // All children except the arrow are shortcuts.
+        return getChildCount() - 1;
+    }
+
+    private void animateOpen() {
+        setVisibility(View.VISIBLE);
+        mIsOpen = true;
+
+        final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet();
+        final int shortcutCount = getShortcutCount();
+
+        final long duration = getResources().getInteger(
+                R.integer.config_deepShortcutOpenDuration);
+        final long arrowScaleDuration = getResources().getInteger(
+                R.integer.config_deepShortcutArrowOpenDuration);
+        final long arrowScaleDelay = duration - arrowScaleDuration;
+        final long stagger = getResources().getInteger(
+                R.integer.config_deepShortcutOpenStagger);
+        final TimeInterpolator fadeInterpolator = new LogAccelerateInterpolator(100, 0);
+
+        // Animate shortcuts
+        DecelerateInterpolator interpolator = new DecelerateInterpolator();
+        for (int i = 0; i < shortcutCount; i++) {
+            final DeepShortcutView deepShortcutView = getShortcutAt(i);
+            deepShortcutView.setVisibility(INVISIBLE);
+            deepShortcutView.setAlpha(0);
+
+            Animator anim = deepShortcutView.createOpenAnimation(mIsAboveIcon, mIsLeftAligned);
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationStart(Animator animation) {
+                    deepShortcutView.setVisibility(VISIBLE);
+                }
+            });
+            anim.setDuration(duration);
+            int animationIndex = mIsAboveIcon ? shortcutCount - i - 1 : i;
+            anim.setStartDelay(stagger * animationIndex);
+            anim.setInterpolator(interpolator);
+            shortcutAnims.play(anim);
+
+            Animator fadeAnim = new LauncherViewPropertyAnimator(deepShortcutView).alpha(1);
+            fadeAnim.setInterpolator(fadeInterpolator);
+            // We want the shortcut to be fully opaque before the arrow starts animating.
+            fadeAnim.setDuration(arrowScaleDelay);
+            shortcutAnims.play(fadeAnim);
+        }
+        shortcutAnims.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mOpenCloseAnimator = null;
+                Utilities.sendCustomAccessibilityEvent(
+                        DeepShortcutsContainer.this,
+                        AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
+                        getContext().getString(R.string.action_deep_shortcut));
+            }
+        });
+
+        // Animate the arrow
+        mArrow.setScaleX(0);
+        mArrow.setScaleY(0);
+        Animator arrowScale = new LauncherViewPropertyAnimator(mArrow).scaleX(1).scaleY(1);
+        arrowScale.setStartDelay(arrowScaleDelay);
+        arrowScale.setDuration(arrowScaleDuration);
+        shortcutAnims.play(arrowScale);
+
+        mOpenCloseAnimator = shortcutAnims;
+        shortcutAnims.start();
+    }
+
+    /**
+     * Orients this container above or below the given icon, aligning with the left or right.
+     *
+     * These are the preferred orientations, in order (RTL prefers right-aligned over left):
+     * - Above and left-aligned
+     * - Above and right-aligned
+     * - Below and left-aligned
+     * - Below and right-aligned
+     *
+     * So we always align left if there is enough horizontal space
+     * and align above if there is enough vertical space.
+     */
+    private void orientAboutIcon(BubbleTextView icon, int arrowHeight) {
+        int width = getMeasuredWidth();
+        int height = getMeasuredHeight() + arrowHeight;
+
+        DragLayer dragLayer = mLauncher.getDragLayer();
+        dragLayer.getDescendantRectRelativeToSelf(icon, mTempRect);
+        Rect insets = dragLayer.getInsets();
+
+        // Align left (right in RTL) if there is room.
+        int leftAlignedX = mTempRect.left + icon.getPaddingLeft();
+        int rightAlignedX = mTempRect.right - width - icon.getPaddingRight();
+        int x = leftAlignedX;
+        boolean canBeLeftAligned = leftAlignedX + width < dragLayer.getRight() - insets.right;
+        boolean canBeRightAligned = rightAlignedX > dragLayer.getLeft() + insets.left;
+        if (!canBeLeftAligned || (mIsRtl && canBeRightAligned)) {
+            x = rightAlignedX;
+        }
+        mIsLeftAligned = x == leftAlignedX;
+        if (mIsRtl) {
+            x -= dragLayer.getWidth() - width;
+        }
+
+        // Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
+        int iconWidth = icon.getWidth() - icon.getTotalPaddingLeft() - icon.getTotalPaddingRight();
+        iconWidth *= icon.getScaleX();
+        Resources resources = getResources();
+        int xOffset;
+        if (isAlignedWithStart()) {
+            // Aligning with the shortcut icon.
+            int shortcutIconWidth = resources.getDimensionPixelSize(R.dimen.deep_shortcut_icon_size);
+            int shortcutPaddingStart = resources.getDimensionPixelSize(
+                    R.dimen.deep_shortcut_padding_start);
+            xOffset = iconWidth / 2 - shortcutIconWidth / 2 - shortcutPaddingStart;
+        } else {
+            // Aligning with the drag handle.
+            int shortcutDragHandleWidth = resources.getDimensionPixelSize(
+                    R.dimen.deep_shortcut_drag_handle_size);
+            int shortcutPaddingEnd = resources.getDimensionPixelSize(
+                    R.dimen.deep_shortcut_padding_end);
+            xOffset = iconWidth / 2 - shortcutDragHandleWidth / 2 - shortcutPaddingEnd;
+        }
+        x += mIsLeftAligned ? xOffset : -xOffset;
+
+        // Open above icon if there is room.
+        int iconHeight = icon.getIcon().getBounds().height();
+        int y = mTempRect.top + icon.getPaddingTop() - height;
+        mIsAboveIcon = y > dragLayer.getTop() + insets.top;
+        if (!mIsAboveIcon) {
+            y = mTempRect.top + icon.getPaddingTop() + iconHeight;
+        }
+
+        // Insets are added later, so subtract them now.
+        y -= insets.top;
+
+        setX(x);
+        setY(y);
+    }
+
+    private boolean isAlignedWithStart() {
+        return mIsLeftAligned && !mIsRtl || !mIsLeftAligned && mIsRtl;
+    }
+
+    /**
+     * Adds an arrow view pointing at the original icon.
+     * @param horizontalOffset the horizontal offset of the arrow, so that it
+     *                              points at the center of the original icon
+     */
+    private View addArrowView(int horizontalOffset, int verticalOffset, int width, int height) {
+        LinearLayout.LayoutParams layoutParams = new LayoutParams(width, height);
+        if (mIsLeftAligned) {
+            layoutParams.gravity = Gravity.LEFT;
+            layoutParams.leftMargin = horizontalOffset;
+        } else {
+            layoutParams.gravity = Gravity.RIGHT;
+            layoutParams.rightMargin = horizontalOffset;
+        }
+        if (mIsAboveIcon) {
+            layoutParams.topMargin = verticalOffset;
+        } else {
+            layoutParams.bottomMargin = verticalOffset;
+        }
+
+        View arrowView = new View(getContext());
+        ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
+                width, height, !mIsAboveIcon));
+        arrowDrawable.getPaint().setColor(Color.WHITE);
+        arrowView.setBackground(arrowDrawable);
+        arrowView.setElevation(getElevation());
+        addView(arrowView, mIsAboveIcon ? getChildCount() : 0, layoutParams);
+        return arrowView;
+    }
+
+    private void deferDrag(BubbleTextView originalIcon) {
+        mDeferredDragIcon = originalIcon;
+        mLauncher.getDragController().addDragListener(this);
+    }
+
+    public BubbleTextView getDeferredDragIcon() {
+        return mDeferredDragIcon;
+    }
+
+    /**
+     * Determines when the deferred drag should be started.
+     *
+     * Current behavior:
+     * - Start the drag if the touch passes a certain distance from the original touch down.
+     */
+    public DragOptions.DeferDragCondition createDeferDragCondition(final Runnable onDragStart) {
+        return new DragOptions.DeferDragCondition() {
+            @Override
+            public boolean shouldStartDeferredDrag(double distanceDragged) {
+                return distanceDragged > mStartDragThreshold;
+            }
+
+            @Override
+            public void onDeferredDragStart() {
+                mDeferredDragIcon.setVisibility(INVISIBLE);
+            }
+
+            @Override
+            public void onDropBeforeDeferredDrag() {
+                mLauncher.getUserEventDispatcher().logDeepShortcutsOpen(mDeferredDragIcon);
+                if (!mIsAboveIcon) {
+                    mDeferredDragIcon.setTextVisibility(false);
+                }
+            }
+
+            @Override
+            public void onDragStart() {
+                if (onDragStart != null) {
+                    onDragStart.run();
+                }
+            }
+        };
+    }
+
+    @Override
+    public boolean onTouch(View v, MotionEvent ev) {
+        // Touched a shortcut, update where it was touched so we can drag from there on long click.
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+            case MotionEvent.ACTION_MOVE:
+                mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY());
+                break;
+        }
+        return false;
+    }
+
+    public boolean onLongClick(View v) {
+        // Return early if this is not initiated from a touch or not the correct view
+        if (!v.isInTouchMode() || !(v.getParent() instanceof DeepShortcutView)) return false;
+        // Return if global dragging is not enabled
+        if (!mLauncher.isDraggingEnabled()) return false;
+
+        // Long clicked on a shortcut.
+        mDeferContainerRemoval = true;
+        DeepShortcutView sv = (DeepShortcutView) v.getParent();
+        sv.setWillDrawIcon(false);
+
+        // Move the icon to align with the center-top of the touch point
+        mIconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x;
+        mIconShift.y = mIconLastTouchPos.y - mLauncher.getDeviceProfile().iconSizePx;
+
+        DragView dv = mLauncher.getWorkspace().beginDragShared(
+                sv.getBubbleText(), this, sv.getFinalInfo(),
+                new ShortcutDragPreviewProvider(sv.getIconView(), mIconShift), new DragOptions());
+        dv.animateShift(-mIconShift.x, -mIconShift.y);
+
+        // TODO: support dragging from within folder without having to close it
+        mLauncher.closeFolder();
+        return false;
+    }
+
+    @Override
+    public boolean supportsFlingToDelete() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsAppInfoDropTarget() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsDeleteDropTarget() {
+        return false;
+    }
+
+    @Override
+    public float getIntrinsicIconScaleFactor() {
+        return 1f;
+    }
+
+    @Override
+    public void onFlingToDeleteCompleted() {
+        // Don't care; ignore.
+    }
+
+    @Override
+    public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
+            boolean success) {
+        if (!success) {
+            d.dragView.remove();
+            mLauncher.showWorkspace(true);
+            mLauncher.getDropTargetBar().onDragEnd();
+        }
+    }
+
+    @Override
+    public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+        // Either the original icon or one of the shortcuts was dragged.
+        // Hide the container, but don't remove it yet because that interferes with touch events.
+        animateClose();
+    }
+
+    @Override
+    public void onDragEnd() {
+        if (!mIsOpen) {
+            if (mOpenCloseAnimator != null) {
+                // Close animation is running.
+                mDeferContainerRemoval = false;
+            } else {
+                // Close animation is not running.
+                if (mDeferContainerRemoval) {
+                    close();
+                }
+            }
+        }
+        mDeferredDragIcon.setVisibility(VISIBLE);
+    }
+
+    @Override
+    public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
+        target.itemType = LauncherLogProto.DEEPSHORTCUT;
+        // TODO: add target.rank
+        targetParent.containerType = LauncherLogProto.DEEPSHORTCUTS;
+    }
+
+    public void animateClose() {
+        if (!mIsOpen) {
+            return;
+        }
+        if (mOpenCloseAnimator != null) {
+            mOpenCloseAnimator.cancel();
+        }
+        mIsOpen = false;
+
+        final AnimatorSet shortcutAnims = LauncherAnimUtils.createAnimatorSet();
+        final int shortcutCount = getShortcutCount();
+        int numOpenShortcuts = 0;
+        for (int i = 0; i < shortcutCount; i++) {
+            if (getShortcutAt(i).isOpenOrOpening()) {
+                numOpenShortcuts++;
+            }
+        }
+        final long duration = getResources().getInteger(
+                R.integer.config_deepShortcutCloseDuration);
+        final long arrowScaleDuration = getResources().getInteger(
+                R.integer.config_deepShortcutArrowOpenDuration);
+        final long stagger = getResources().getInteger(
+                R.integer.config_deepShortcutCloseStagger);
+        final TimeInterpolator fadeInterpolator = new LogAccelerateInterpolator(100, 0);
+
+        int firstOpenShortcutIndex = mIsAboveIcon ? shortcutCount - numOpenShortcuts : 0;
+        for (int i = firstOpenShortcutIndex; i < firstOpenShortcutIndex + numOpenShortcuts; i++) {
+            final DeepShortcutView view = getShortcutAt(i);
+            Animator anim;
+            if (view.willDrawIcon()) {
+                anim = view.createCloseAnimation(mIsAboveIcon, mIsLeftAligned, duration);
+                int animationIndex = mIsAboveIcon ? i - firstOpenShortcutIndex
+                        : numOpenShortcuts - i - 1;
+                anim.setStartDelay(stagger * animationIndex);
+
+                Animator fadeAnim = new LauncherViewPropertyAnimator(view).alpha(0);
+                // Don't start fading until the arrow is gone.
+                fadeAnim.setStartDelay(stagger * animationIndex + arrowScaleDuration);
+                fadeAnim.setDuration(duration - arrowScaleDuration);
+                fadeAnim.setInterpolator(fadeInterpolator);
+                shortcutAnims.play(fadeAnim);
+            } else {
+                // The view is being dragged. Animate it such that it collapses with the drag view
+                anim = view.collapseToIcon();
+                anim.setDuration(DragView.VIEW_ZOOM_DURATION);
+
+                // Scale and translate the view to follow the drag view.
+                Point iconCenter = view.getIconCenter();
+                view.setPivotX(iconCenter.x);
+                view.setPivotY(iconCenter.y);
+
+                float scale = ((float) mLauncher.getDeviceProfile().iconSizePx) / view.getHeight();
+                LauncherViewPropertyAnimator anim2 = new LauncherViewPropertyAnimator(view)
+                        .scaleX(scale)
+                        .scaleY(scale)
+                        .translationX(mIconShift.x)
+                        .translationY(mIconShift.y);
+                anim2.setDuration(DragView.VIEW_ZOOM_DURATION);
+                shortcutAnims.play(anim2);
+            }
+            anim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    view.setVisibility(INVISIBLE);
+                }
+            });
+            shortcutAnims.play(anim);
+        }
+        Animator arrowAnim = new LauncherViewPropertyAnimator(mArrow)
+                .scaleX(0).scaleY(0).setDuration(arrowScaleDuration);
+        arrowAnim.setStartDelay(0);
+        shortcutAnims.play(arrowAnim);
+
+        shortcutAnims.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mOpenCloseAnimator = null;
+                if (mDeferContainerRemoval) {
+                    setVisibility(INVISIBLE);
+                } else {
+                    close();
+                }
+            }
+        });
+        mOpenCloseAnimator = shortcutAnims;
+        shortcutAnims.start();
+    }
+
+    /**
+     * Closes the folder without animation.
+     */
+    public void close() {
+        if (mOpenCloseAnimator != null) {
+            mOpenCloseAnimator.cancel();
+            mOpenCloseAnimator = null;
+        }
+        mIsOpen = false;
+        mDeferContainerRemoval = false;
+        boolean isInHotseat = ((ItemInfo) mDeferredDragIcon.getTag()).container
+                == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+        mDeferredDragIcon.setTextVisibility(!isInHotseat);
+        mLauncher.getDragController().removeDragListener(this);
+        mLauncher.getDragLayer().removeView(this);
+    }
+
+    public boolean isOpen() {
+        return mIsOpen;
+    }
+
+    /**
+     * Shows the shortcuts container for {@param icon}
+     * @return the container if shown or null.
+     */
+    public static DeepShortcutsContainer showForIcon(BubbleTextView icon) {
+        Launcher launcher = Launcher.getLauncher(icon.getContext());
+        if (launcher.getOpenShortcutsContainer() != null) {
+            // There is already a shortcuts container open, so don't open this one.
+            icon.clearFocus();
+            return null;
+        }
+        List<String> ids = launcher.getShortcutIdsForItem((ItemInfo) icon.getTag());
+        if (!ids.isEmpty()) {
+            // There are shortcuts associated with the app, so defer its drag.
+            final DeepShortcutsContainer container =
+                    (DeepShortcutsContainer) launcher.getLayoutInflater().inflate(
+                            R.layout.deep_shortcuts_container, launcher.getDragLayer(), false);
+            container.setVisibility(View.INVISIBLE);
+            launcher.getDragLayer().addView(container);
+            container.populateAndShow(icon, ids);
+            return container;
+        }
+        return null;
+    }
+
+    /**
+     * Extension of {@link ShortcutInfo} which does not badge the icons.
+     */
+    static class UnbadgedShortcutInfo extends ShortcutInfo {
+        public final ShortcutInfoCompat mDetail;
+
+        public UnbadgedShortcutInfo(ShortcutInfoCompat shortcutInfo, Context context) {
+            super(shortcutInfo, context);
+            mDetail = shortcutInfo;
+        }
+
+        @Override
+        protected Bitmap getBadgedIcon(Bitmap unbadgedBitmap, ShortcutInfoCompat shortcutInfo,
+                IconCache cache, Context context) {
+            return unbadgedBitmap;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutCache.java b/src/com/android/launcher3/shortcuts/ShortcutCache.java
new file mode 100644
index 0000000..d4db96d
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/ShortcutCache.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.shortcuts;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.os.UserHandle;
+import android.util.LruCache;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Loads {@link ShortcutInfoCompat}s on demand (e.g. when launcher
+ * loads for pinned shortcuts and on long-press for dynamic shortcuts), and caches them
+ * for handful of apps in an LruCache while launcher lives.
+ */
+@TargetApi(Build.VERSION_CODES.N)
+public class ShortcutCache {
+    private static final String TAG = "ShortcutCache";
+    private static final boolean LOGD = false;
+
+    private static final int CACHE_SIZE = 30; // Max number shortcuts we cache.
+
+    private LruCache<ShortcutKey, ShortcutInfoCompat> mCachedShortcuts;
+    // We always keep pinned shortcuts in the cache.
+    private HashMap<ShortcutKey, ShortcutInfoCompat> mPinnedShortcuts;
+
+    public ShortcutCache() {
+        mCachedShortcuts = new LruCache<>(CACHE_SIZE);
+        mPinnedShortcuts = new HashMap<>();
+    }
+
+    /**
+     * Removes shortcuts from the cache when shortcuts change for a given package.
+     *
+     * Returns a map of ids to their evicted shortcuts.
+     *
+     * @see android.content.pm.LauncherApps.Callback#onShortcutsChanged(String, List, UserHandle).
+     */
+    public void removeShortcuts(List<ShortcutInfoCompat> shortcuts) {
+        for (ShortcutInfoCompat shortcut : shortcuts) {
+            ShortcutKey key = ShortcutKey.fromInfo(shortcut);
+            mCachedShortcuts.remove(key);
+            mPinnedShortcuts.remove(key);
+        }
+    }
+
+    public ShortcutInfoCompat get(ShortcutKey key) {
+        if (mPinnedShortcuts.containsKey(key)) {
+            return mPinnedShortcuts.get(key);
+        }
+        return mCachedShortcuts.get(key);
+    }
+
+    public void put(ShortcutKey key, ShortcutInfoCompat shortcut) {
+        if (shortcut.isPinned()) {
+            mPinnedShortcuts.put(key, shortcut);
+        } else {
+            mCachedShortcuts.put(key, shortcut);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
new file mode 100644
index 0000000..2adb82e
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.shortcuts;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+
+import com.android.launcher3.HolographicOutlineHelper;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.graphics.DragPreviewProvider;
+
+/**
+ * Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size.
+ */
+public class ShortcutDragPreviewProvider extends DragPreviewProvider {
+
+    private final Point mPositionShift;
+
+    public ShortcutDragPreviewProvider(View icon, Point shift) {
+        super(icon);
+        mPositionShift = shift;
+    }
+
+    @Override
+    public Bitmap createDragOutline(Canvas canvas) {
+        Bitmap b = drawScaledPreview(canvas, Bitmap.Config.ALPHA_8);
+
+        HolographicOutlineHelper.obtain(mView.getContext())
+                .applyExpensiveOutlineWithBlur(b, canvas);
+        canvas.setBitmap(null);
+        return b;
+    }
+
+    @Override
+    public Bitmap createDragBitmap(Canvas canvas) {
+        Bitmap b = drawScaledPreview(canvas, Bitmap.Config.ARGB_8888);
+        canvas.setBitmap(null);
+        return b;
+    }
+
+    private Bitmap drawScaledPreview(Canvas canvas, Bitmap.Config config) {
+        Drawable d = mView.getBackground();
+        Rect bounds = getDrawableBounds(d);
+
+        int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
+
+        final Bitmap b = Bitmap.createBitmap(
+                size + DRAG_BITMAP_PADDING,
+                size + DRAG_BITMAP_PADDING,
+                config);
+
+        canvas.setBitmap(b);
+        canvas.save(Canvas.MATRIX_SAVE_FLAG);
+        canvas.translate(DRAG_BITMAP_PADDING / 2, DRAG_BITMAP_PADDING / 2);
+        canvas.scale(((float) size) / bounds.width(), ((float) size) / bounds.height(), 0, 0);
+        canvas.translate(bounds.left, bounds.top);
+        d.draw(canvas);
+        canvas.restore();
+        return b;
+    }
+
+    @Override
+    public float getScaleAndPosition(Bitmap preview, int[] outPos) {
+        Launcher launcher = Launcher.getLauncher(mView.getContext());
+        int iconSize = getDrawableBounds(mView.getBackground()).width();
+        float scale = launcher.getDragLayer().getLocationInDragLayer(mView, outPos);
+
+        int iconLeft = mView.getPaddingStart();
+        if (Utilities.isRtl(mView.getResources())) {
+            iconLeft = mView.getWidth() - iconSize - iconLeft;
+        }
+
+        outPos[0] += Math.round(scale * iconLeft + (scale * iconSize - preview.getWidth()) / 2 +
+                mPositionShift.x);
+        outPos[1] += Math.round((scale * mView.getHeight() - preview.getHeight()) / 2
+                + mPositionShift.y);
+        float size = launcher.getDeviceProfile().iconSizePx;
+        return scale * iconSize / size;
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutFilter.java b/src/com/android/launcher3/shortcuts/ShortcutFilter.java
new file mode 100644
index 0000000..ec68817
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/ShortcutFilter.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.shortcuts;
+
+import android.support.annotation.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Sorts and filters shortcuts.
+ */
+public class ShortcutFilter {
+
+    public static final int MAX_SHORTCUTS = 4;
+    @VisibleForTesting static final int NUM_DYNAMIC = 2;
+
+    /**
+     * Sorts shortcuts in rank order, with manifest shortcuts coming before dynamic shortcuts.
+     */
+    private static final Comparator<ShortcutInfoCompat> RANK_COMPARATOR
+            = new Comparator<ShortcutInfoCompat>() {
+        @Override
+        public int compare(ShortcutInfoCompat a, ShortcutInfoCompat b) {
+            if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) {
+                return -1;
+            }
+            if (!a.isDeclaredInManifest() && b.isDeclaredInManifest()) {
+                return 1;
+            }
+            return Integer.compare(a.getRank(), b.getRank());
+        }
+    };
+
+    /**
+     * Filters the shortcuts so that only MAX_SHORTCUTS or fewer shortcuts are retained.
+     * We want the filter to include both static and dynamic shortcuts, so we always
+     * include NUM_DYNAMIC dynamic shortcuts, if at least that many are present.
+     *
+     * @return a subset of shortcuts, in sorted order, with size <= MAX_SHORTCUTS.
+     */
+    public static List<ShortcutInfoCompat> sortAndFilterShortcuts(
+            List<ShortcutInfoCompat> shortcuts) {
+        Collections.sort(shortcuts, RANK_COMPARATOR);
+        if (shortcuts.size() <= MAX_SHORTCUTS) {
+            return shortcuts;
+        }
+
+        // The list of shortcuts is now sorted with static shortcuts followed by dynamic
+        // shortcuts. We want to preserve this order, but only keep MAX_SHORTCUTS.
+        List<ShortcutInfoCompat> filteredShortcuts = new ArrayList<>(MAX_SHORTCUTS);
+        int numDynamic = 0;
+        int size = shortcuts.size();
+        for (int i = 0; i < size; i++) {
+            ShortcutInfoCompat shortcut = shortcuts.get(i);
+            int filteredSize = filteredShortcuts.size();
+            if (filteredSize < MAX_SHORTCUTS) {
+                // Always add the first MAX_SHORTCUTS to the filtered list.
+                filteredShortcuts.add(shortcut);
+                if (shortcut.isDynamic()) {
+                    numDynamic++;
+                }
+                continue;
+            }
+            // At this point, we have MAX_SHORTCUTS already, but they may all be static.
+            // If there are dynamic shortcuts, remove static shortcuts to add them.
+            if (shortcut.isDynamic() && numDynamic < NUM_DYNAMIC) {
+                numDynamic++;
+                int lastStaticIndex = filteredSize - numDynamic;
+                filteredShortcuts.remove(lastStaticIndex);
+                filteredShortcuts.add(shortcut);
+            }
+        }
+        return filteredShortcuts;
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
new file mode 100644
index 0000000..a6da668
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/ShortcutInfoCompat.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.shortcuts;
+
+import android.annotation.TargetApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.os.Build;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.compat.DeferredLauncherActivityInfo;
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+
+/**
+ * Wrapper class for {@link android.content.pm.ShortcutInfo}, representing deep shortcuts into apps.
+ *
+ * Not to be confused with {@link com.android.launcher3.ShortcutInfo}.
+ */
+@TargetApi(Build.VERSION_CODES.N)
+public class ShortcutInfoCompat {
+    private static final String INTENT_CATEGORY = "com.android.launcher3.DEEP_SHORTCUT";
+    public static final String EXTRA_SHORTCUT_ID = "shortcut_id";
+
+    private ShortcutInfo mShortcutInfo;
+
+    public ShortcutInfoCompat(ShortcutInfo shortcutInfo) {
+        mShortcutInfo = shortcutInfo;
+    }
+
+    @TargetApi(Build.VERSION_CODES.N)
+    public Intent makeIntent(Context context) {
+        long serialNumber = UserManagerCompat.getInstance(context)
+                .getSerialNumberForUser(getUserHandle());
+        return new Intent(Intent.ACTION_MAIN)
+                .addCategory(INTENT_CATEGORY)
+                .setComponent(getActivity())
+                .setPackage(getPackage())
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
+                .putExtra(ItemInfo.EXTRA_PROFILE, serialNumber)
+                .putExtra(EXTRA_SHORTCUT_ID, getId());
+    }
+
+    public ShortcutInfo getShortcutInfo() {
+        return mShortcutInfo;
+    }
+
+    public String getPackage() {
+        return mShortcutInfo.getPackage();
+    }
+
+    public String getId() {
+        return mShortcutInfo.getId();
+    }
+
+    public CharSequence getShortLabel() {
+        return mShortcutInfo.getShortLabel();
+    }
+
+    public CharSequence getLongLabel() {
+        return mShortcutInfo.getLongLabel();
+    }
+
+    public long getLastChangedTimestamp() {
+        return mShortcutInfo.getLastChangedTimestamp();
+    }
+
+    public ComponentName getActivity() {
+        return mShortcutInfo.getActivity();
+    }
+
+    public UserHandleCompat getUserHandle() {
+        return UserHandleCompat.fromUser(mShortcutInfo.getUserHandle());
+    }
+
+    public boolean hasKeyFieldsOnly() {
+        return mShortcutInfo.hasKeyFieldsOnly();
+    }
+
+    public boolean isPinned() {
+        return mShortcutInfo.isPinned();
+    }
+
+    public boolean isDeclaredInManifest() {
+        return mShortcutInfo.isDeclaredInManifest();
+    }
+
+    public boolean isEnabled() {
+        return mShortcutInfo.isEnabled();
+    }
+
+    public boolean isDynamic() {
+        return mShortcutInfo.isDynamic();
+    }
+
+    public int getRank() {
+        return mShortcutInfo.getRank();
+    }
+
+    public CharSequence getDisabledMessage() {
+        return mShortcutInfo.getDisabledMessage();
+    }
+
+    @Override
+    public String toString() {
+        return mShortcutInfo.toString();
+    }
+
+    public LauncherActivityInfoCompat getActivityInfo(Context context) {
+        return new DeferredLauncherActivityInfo(getActivity(), getUserHandle(), context);
+    }
+}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java
new file mode 100644
index 0000000..a219c54
--- /dev/null
+++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java
@@ -0,0 +1,38 @@
+package com.android.launcher3.shortcuts;
+
+import android.content.ComponentName;
+import android.content.Intent;
+
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.ComponentKey;
+
+/**
+ * A key that uniquely identifies a shortcut using its package, id, and user handle.
+ */
+public class ShortcutKey extends ComponentKey {
+
+    public ShortcutKey(String packageName, UserHandleCompat user, String id) {
+        // Use the id as the class name.
+        super(new ComponentName(packageName, id), user);
+    }
+
+    public String getId() {
+        return componentName.getClassName();
+    }
+
+    public static ShortcutKey fromInfo(ShortcutInfoCompat shortcutInfo) {
+        return new ShortcutKey(shortcutInfo.getPackage(), shortcutInfo.getUserHandle(),
+                shortcutInfo.getId());
+    }
+
+    public static ShortcutKey fromIntent(Intent intent, UserHandleCompat user) {
+        String shortcutId = intent.getStringExtra(
+                ShortcutInfoCompat.EXTRA_SHORTCUT_ID);
+        return new ShortcutKey(intent.getPackage(), user, shortcutId);
+    }
+
+    public static ShortcutKey fromShortcutInfo(ShortcutInfo info) {
+        return fromIntent(info.getPromisedIntent(), info.user);
+    }
+}
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index 475762f..6797c7b 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -12,6 +12,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherCallbacks;
 import com.android.launcher3.allapps.AllAppsSearchBarController;
+import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.util.ComponentKey;
 
 import java.io.FileDescriptor;
@@ -122,50 +123,14 @@
         }
 
         @Override
-        public void onClickAllAppsButton(View v) {
-        }
-
-        @Override
         public void bindAllApplications(ArrayList<AppInfo> apps) {
         }
 
         @Override
-        public void onClickFolderIcon(View v) {
-        }
-
-        @Override
-        public void onClickAppShortcut(View v) {
-        }
-
-        @Override
-        public void onClickPagedViewIcon(View v) {
-        }
-
-        @Override
-        public void onClickWallpaperPicker(View v) {
-        }
-
-        @Override
-        public void onClickSettingsButton(View v) {
-        }
-
-        @Override
-        public void onClickAddWidgetButton(View v) {
-        }
-
-        @Override
-        public void onPageSwitch(View newPage, int newPageIndex) {
-        }
-
-        @Override
         public void onWorkspaceLockedChanged() {
         }
 
         @Override
-        public void onDragStarted(View view) {
-        }
-
-        @Override
         public void onInteractionBegin() {
         }
 
@@ -174,18 +139,8 @@
         }
 
         @Override
-        public boolean providesSearch() {
-            return false;
-        }
-
-        @Override
         public boolean startSearch(String initialQuery, boolean selectInitialQuery,
-                Bundle appSearchData, Rect sourceBounds) {
-            return false;
-        }
-
-        @Override
-        public boolean startSearchFromAllApps(String query) {
+                Bundle appSearchData) {
             return false;
         }
 
@@ -225,6 +180,9 @@
         }
 
         @Override
+        public UserEventDispatcher getUserEventDispatcher() { return null; }
+
+        @Override
         public View getQsbBar() {
             return null;
         }
@@ -235,26 +193,6 @@
         }
 
         @Override
-        public Intent getFirstRunActivity() {
-            return null;
-        }
-
-        @Override
-        public boolean hasFirstRunActivity() {
-            return false;
-        }
-
-        @Override
-        public boolean hasDismissableIntroScreen() {
-            return false;
-        }
-
-        @Override
-        public View getIntroScreen() {
-            return null;
-        }
-
-        @Override
         public boolean shouldMoveToDefaultScreenOnHomeIntent() {
             return true;
         }
@@ -265,17 +203,13 @@
         }
 
         @Override
-        public boolean overrideWallpaperDimensions() {
-            return false;
-        }
-
-        @Override
         public AllAppsSearchBarController getAllAppsSearchBarController() {
             return null;
         }
 
         @Override
         public List<ComponentKey> getPredictedApps() {
+            // To debug app predictions, enable AlphabeticalAppsList#DEBUG_PREDICTIONS
             return new ArrayList<>();
         }
 
@@ -285,11 +219,6 @@
         }
 
         @Override
-        public boolean isLauncherPreinstalled() {
-            return false;
-        }
-
-        @Override
         public void setLauncherSearchCallback(Object callbacks) {
             // Do nothing
         }
@@ -300,6 +229,11 @@
 
         @Override
         public void onDetachedFromWindow() {
-        };
+        }
+
+        @Override
+        public boolean shouldShowDiscoveryBounce() {
+            return false;
+        }
     }
 }
diff --git a/src/com/android/launcher3/util/ActivityResultInfo.java b/src/com/android/launcher3/util/ActivityResultInfo.java
new file mode 100644
index 0000000..2261bee
--- /dev/null
+++ b/src/com/android/launcher3/util/ActivityResultInfo.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Utility class which stores information from onActivityResult
+ */
+public class ActivityResultInfo implements Parcelable {
+
+    public final int requestCode;
+    public final int resultCode;
+    public final Intent data;
+
+    public ActivityResultInfo(int requestCode, int resultCode, Intent data) {
+        this.requestCode = requestCode;
+        this.resultCode = resultCode;
+        this.data = data;
+    }
+
+    private ActivityResultInfo(Parcel parcel) {
+        requestCode = parcel.readInt();
+        resultCode = parcel.readInt();
+        data = parcel.readInt() != 0 ? Intent.CREATOR.createFromParcel(parcel) : null;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(requestCode);
+        dest.writeInt(resultCode);
+        if (data != null) {
+            dest.writeInt(1);
+            data.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
+    }
+
+    public static final Parcelable.Creator<ActivityResultInfo> CREATOR =
+            new Parcelable.Creator<ActivityResultInfo>() {
+                public ActivityResultInfo createFromParcel(Parcel source) {
+                    return new ActivityResultInfo(source);
+                }
+
+                public ActivityResultInfo[] newArray(int size) {
+                    return new ActivityResultInfo[size];
+                }
+            };
+}
diff --git a/src/com/android/launcher3/util/CachedPackageTracker.java b/src/com/android/launcher3/util/CachedPackageTracker.java
new file mode 100644
index 0000000..293714e
--- /dev/null
+++ b/src/com/android/launcher3/util/CachedPackageTracker.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.LauncherAppsCompat.OnAppsChangedCallbackCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Utility class to track list of installed packages. It persists the list so that apps
+ * installed/uninstalled while Launcher was dead can also be handled properly.
+ */
+public abstract class CachedPackageTracker implements OnAppsChangedCallbackCompat {
+
+    protected static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_";
+
+    protected final SharedPreferences mPrefs;
+    protected final UserManagerCompat mUserManager;
+    protected final LauncherAppsCompat mLauncherApps;
+
+    public CachedPackageTracker(Context context, String preferenceFileName) {
+        mPrefs = context.getSharedPreferences(preferenceFileName, Context.MODE_PRIVATE);
+        mUserManager = UserManagerCompat.getInstance(context);
+        mLauncherApps = LauncherAppsCompat.getInstance(context);
+    }
+
+    /**
+     * Checks the list of user apps, and generates package event accordingly.
+     * {@see #onLauncherAppsAdded}, {@see #onLauncherPackageRemoved}
+     */
+    public void processUserApps(List<LauncherActivityInfoCompat> apps, UserHandleCompat user) {
+        String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
+        HashSet<String> oldPackageSet = new HashSet<>();
+        final boolean userAppsExisted = getUserApps(oldPackageSet, prefKey);
+
+        HashSet<String> packagesRemoved = new HashSet<>(oldPackageSet);
+        HashSet<String> newPackageSet = new HashSet<>();
+        ArrayList<LauncherActivityInstallInfo> packagesAdded = new ArrayList<>();
+
+        for (LauncherActivityInfoCompat info : apps) {
+            String packageName = info.getComponentName().getPackageName();
+            newPackageSet.add(packageName);
+            packagesRemoved.remove(packageName);
+
+            if (!oldPackageSet.contains(packageName)) {
+                oldPackageSet.add(packageName);
+                packagesAdded.add(new LauncherActivityInstallInfo(
+                        info, info.getFirstInstallTime()));
+            }
+        }
+
+        if (!packagesAdded.isEmpty() || !packagesRemoved.isEmpty()) {
+            mPrefs.edit().putStringSet(prefKey, newPackageSet).apply();
+
+            if (!packagesAdded.isEmpty()) {
+                Collections.sort(packagesAdded);
+                onLauncherAppsAdded(packagesAdded, user, userAppsExisted);
+            }
+
+            if (!packagesRemoved.isEmpty()) {
+                for (String pkg : packagesRemoved) {
+                    onLauncherPackageRemoved(pkg, user);
+                }
+            }
+        }
+    }
+
+    /**
+     * Reads the list of user apps which have already been processed.
+     * @return false if the list didn't exist, true otherwise
+     */
+    private boolean getUserApps(HashSet<String> outExistingApps, String prefKey) {
+        Set<String> userApps = mPrefs.getStringSet(prefKey, null);
+        if (userApps == null) {
+            return false;
+        } else {
+            outExistingApps.addAll(userApps);
+            return true;
+        }
+    }
+
+    @Override
+    public void onPackageRemoved(String packageName, UserHandleCompat user) {
+        String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
+        HashSet<String> packageSet = new HashSet<>();
+        if (getUserApps(packageSet, prefKey) && packageSet.remove(packageName)) {
+            mPrefs.edit().putStringSet(prefKey, packageSet).apply();
+        }
+
+        onLauncherPackageRemoved(packageName, user);
+    }
+
+    @Override
+    public void onPackageAdded(String packageName, UserHandleCompat user) {
+        String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
+        HashSet<String> packageSet = new HashSet<>();
+        final boolean userAppsExisted = getUserApps(packageSet, prefKey);
+        if (!packageSet.contains(packageName)) {
+            List<LauncherActivityInfoCompat> activities =
+                    mLauncherApps.getActivityList(packageName, user);
+            if (!activities.isEmpty()) {
+                LauncherActivityInfoCompat activityInfo = activities.get(0);
+
+                packageSet.add(packageName);
+                mPrefs.edit().putStringSet(prefKey, packageSet).apply();
+                onLauncherAppsAdded(Arrays.asList(
+                        new LauncherActivityInstallInfo(activityInfo, System.currentTimeMillis())),
+                        user, userAppsExisted);
+            }
+        }
+    }
+
+    @Override
+    public void onPackageChanged(String packageName, UserHandleCompat user) { }
+
+    @Override
+    public void onPackagesAvailable(
+            String[] packageNames, UserHandleCompat user, boolean replacing) { }
+
+    @Override
+    public void onPackagesUnavailable(
+            String[] packageNames, UserHandleCompat user, boolean replacing) { }
+
+    @Override
+    public void onPackagesSuspended(String[] packageNames, UserHandleCompat user) { }
+
+    @Override
+    public void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user) { }
+
+    /**
+     * Called when new launcher apps are added.
+     * @param apps list of newly added activities. Only one entry per package is sent.
+     * @param user the user for this event. All activities in {@param apps} will belong to
+     *             the same user.
+     * @param userAppsExisted false if the list was processed for the first time, like in case
+     *                        when Launcher was newly installed or a new user was added.
+     */
+    protected abstract void onLauncherAppsAdded(List<LauncherActivityInstallInfo> apps,
+            UserHandleCompat user, boolean userAppsExisted);
+
+    /**
+     * Called when apps are removed from the system.
+     */
+    protected abstract void onLauncherPackageRemoved(String packageName, UserHandleCompat user);
+
+    public static class LauncherActivityInstallInfo
+            implements Comparable<LauncherActivityInstallInfo> {
+        public final LauncherActivityInfoCompat info;
+        public final long installTime;
+
+        public LauncherActivityInstallInfo(LauncherActivityInfoCompat info, long installTime) {
+            this.info = info;
+            this.installTime = installTime;
+        }
+
+        @Override
+        public int compareTo(LauncherActivityInstallInfo another) {
+            return Utilities.longCompare(installTime, another.installTime);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/CellAndSpan.java b/src/com/android/launcher3/util/CellAndSpan.java
new file mode 100644
index 0000000..3a0a6cd
--- /dev/null
+++ b/src/com/android/launcher3/util/CellAndSpan.java
@@ -0,0 +1,48 @@
+package com.android.launcher3.util;
+
+/**
+ * Base class which represents an area on the grid.
+ */
+public class CellAndSpan {
+
+    /**
+     * Indicates the X position of the associated cell.
+     */
+    public int cellX = -1;
+
+    /**
+     * Indicates the Y position of the associated cell.
+     */
+    public int cellY = -1;
+
+    /**
+     * Indicates the X cell span.
+     */
+    public int spanX = 1;
+
+    /**
+     * Indicates the Y cell span.
+     */
+    public int spanY = 1;
+
+    public CellAndSpan() {
+    }
+
+    public void copyFrom(CellAndSpan copy) {
+        cellX = copy.cellX;
+        cellY = copy.cellY;
+        spanX = copy.spanX;
+        spanY = copy.spanY;
+    }
+
+    public CellAndSpan(int cellX, int cellY, int spanX, int spanY) {
+        this.cellX = cellX;
+        this.cellY = cellY;
+        this.spanX = spanX;
+        this.spanY = spanY;
+    }
+
+    public String toString() {
+        return "(" + cellX + ", " + cellY + ": " + spanX + ", " + spanY + ")";
+    }
+}
diff --git a/src/com/android/launcher3/util/CircleRevealOutlineProvider.java b/src/com/android/launcher3/util/CircleRevealOutlineProvider.java
new file mode 100644
index 0000000..c192120
--- /dev/null
+++ b/src/com/android/launcher3/util/CircleRevealOutlineProvider.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class CircleRevealOutlineProvider extends RevealOutlineAnimation {
+
+    private int mCenterX;
+    private int mCenterY;
+    private float mRadius0;
+    private float mRadius1;
+
+    /**
+     * @param x reveal center x
+     * @param y reveal center y
+     * @param r0 initial radius
+     * @param r1 final radius
+     */
+    public CircleRevealOutlineProvider(int x, int y, float r0, float r1) {
+        mCenterX = x;
+        mCenterY = y;
+        mRadius0 = r0;
+        mRadius1 = r1;
+    }
+
+    @Override
+    public boolean shouldRemoveElevationDuringAnimation() {
+        return true;
+    }
+
+    @Override
+    public void setProgress(float progress) {
+        mOutlineRadius = (1 - progress) * mRadius0 + progress * mRadius1;
+
+        mOutline.left = (int) (mCenterX - mOutlineRadius);
+        mOutline.top = (int) (mCenterY - mOutlineRadius);
+        mOutline.right = (int) (mCenterX + mOutlineRadius);
+        mOutline.bottom = (int) (mCenterY + mOutlineRadius);
+    }
+}
diff --git a/src/com/android/launcher3/util/ComponentKey.java b/src/com/android/launcher3/util/ComponentKey.java
index b7aafae..5882f21 100644
--- a/src/com/android/launcher3/util/ComponentKey.java
+++ b/src/com/android/launcher3/util/ComponentKey.java
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.content.Context;
+
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 
@@ -31,8 +32,8 @@
     private final int mHashCode;
 
     public ComponentKey(ComponentName componentName, UserHandleCompat user) {
-        assert (componentName != null);
-        assert (user != null);
+        Preconditions.assertNotNull(componentName);
+        Preconditions.assertNotNull(user);
         this.componentName = componentName;
         this.user = user;
         mHashCode = Arrays.hashCode(new Object[] {componentName, user});
@@ -57,20 +58,11 @@
             componentName = ComponentName.unflattenFromString(componentKeyStr);
             user = UserHandleCompat.myUserHandle();
         }
+        Preconditions.assertNotNull(componentName);
+        Preconditions.assertNotNull(user);
         mHashCode = Arrays.hashCode(new Object[] {componentName, user});
     }
 
-    /**
-     * Encodes a component key as a string of the form [flattenedComponentString#userId].
-     */
-    public String flattenToString(Context context) {
-        String flattened = componentName.flattenToString();
-        if (user != null) {
-            flattened += "#" + UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
-        }
-        return flattened;
-    }
-
     @Override
     public int hashCode() {
         return mHashCode;
@@ -81,4 +73,12 @@
         ComponentKey other = (ComponentKey) o;
         return other.componentName.equals(componentName) && other.user.equals(user);
     }
+
+    /**
+     * Encodes a component key as a string of the form [flattenedComponentString#userId].
+     */
+    @Override
+    public String toString() {
+        return componentName.flattenToString() + "#" + user;
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/util/CursorIconInfo.java b/src/com/android/launcher3/util/CursorIconInfo.java
index cdf9e3c..4fefa98 100644
--- a/src/com/android/launcher3/util/CursorIconInfo.java
+++ b/src/com/android/launcher3/util/CursorIconInfo.java
@@ -30,41 +30,56 @@
  * Utility class to load icon from a cursor.
  */
 public class CursorIconInfo {
-    public final int iconTypeIndex;
     public final int iconPackageIndex;
     public final int iconResourceIndex;
     public final int iconIndex;
 
-    public CursorIconInfo(Cursor c) {
-        iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
+    public final int titleIndex;
+
+    private final Context mContext;
+
+    public CursorIconInfo(Context context, Cursor c) {
+        mContext = context;
+
         iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
         iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
         iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
+
+        titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
     }
 
-    public Bitmap loadIcon(Cursor c, ShortcutInfo info, Context context) {
+    /**
+     * Loads the icon from the cursor and updates the {@param info} if the icon is an app resource.
+     */
+    public Bitmap loadIcon(Cursor c, ShortcutInfo info) {
         Bitmap icon = null;
-        int iconType = c.getInt(iconTypeIndex);
-        switch (iconType) {
-        case LauncherSettings.Favorites.ICON_TYPE_RESOURCE:
-            String packageName = c.getString(iconPackageIndex);
-            String resourceName = c.getString(iconResourceIndex);
-            if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
-                info.iconResource = new ShortcutIconResource();
-                info.iconResource.packageName = packageName;
-                info.iconResource.resourceName = resourceName;
-                icon = Utilities.createIconBitmap(packageName, resourceName, context);
-            }
-            if (icon == null) {
-                // Failed to load from resource, try loading from DB.
-                icon = Utilities.createIconBitmap(c, iconIndex, context);
-            }
-            break;
-        case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
-            icon = Utilities.createIconBitmap(c, iconIndex, context);
-            info.customIcon = icon != null;
-            break;
+        String packageName = c.getString(iconPackageIndex);
+        String resourceName = c.getString(iconResourceIndex);
+        if (!TextUtils.isEmpty(packageName) || !TextUtils.isEmpty(resourceName)) {
+            info.iconResource = new ShortcutIconResource();
+            info.iconResource.packageName = packageName;
+            info.iconResource.resourceName = resourceName;
+            icon = Utilities.createIconBitmap(packageName, resourceName, mContext);
+        }
+        if (icon == null) {
+            // Failed to load from resource, try loading from DB.
+            icon = loadIcon(c);
         }
         return icon;
     }
+
+    /**
+     * Loads the fixed bitmap from the icon if available.
+     */
+    public Bitmap loadIcon(Cursor c) {
+        return Utilities.createIconBitmap(c, iconIndex, mContext);
+    }
+
+    /**
+     * Returns the title or empty string
+     */
+    public String getTitle(Cursor c) {
+        String title = c.getString(titleIndex);
+        return TextUtils.isEmpty(title) ? "" : Utilities.trim(c.getString(titleIndex));
+    }
 }
diff --git a/src/com/android/launcher3/util/FlingAnimation.java b/src/com/android/launcher3/util/FlingAnimation.java
index 55c5d7d..da8bae7 100644
--- a/src/com/android/launcher3/util/FlingAnimation.java
+++ b/src/com/android/launcher3/util/FlingAnimation.java
@@ -7,9 +7,9 @@
 import android.graphics.Rect;
 import android.view.animation.DecelerateInterpolator;
 
-import com.android.launcher3.DragLayer;
-import com.android.launcher3.DragView;
 import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragView;
 
 public class FlingAnimation implements AnimatorUpdateListener {
 
@@ -51,7 +51,7 @@
         mFrom.top += yOffset;
         mFrom.bottom -= yOffset;
 
-        mDuration = initDuration();
+        mDuration = Math.abs(vel.y) > Math.abs(vel.x) ? initFlingUpDuration() : initFlingLeftDuration();
         mAnimationTimeFraction = ((float) mDuration) / (mDuration + DRAG_END_DELAY);
     }
 
@@ -62,7 +62,7 @@
      *   - Calculate a constant acceleration in x direction such that the object reaches
      *     {@link #mIconRect} in the given time.
      */
-    protected int initDuration() {
+    protected int initFlingUpDuration() {
         float sY = -mFrom.bottom;
 
         float d = mUY * mUY + 2 * sY * MAX_ACCELERATION;
@@ -83,6 +83,34 @@
         return (int) Math.round(t);
     }
 
+    /**
+     * The fling animation is based on the following system
+     *   - Apply a constant force in the x direction to causing the fling to decelerate.
+     *   - The animation runs for the time taken by the object to go out of the screen.
+     *   - Calculate a constant acceleration in y direction such that the object reaches
+     *     {@link #mIconRect} in the given time.
+     */
+    protected int initFlingLeftDuration() {
+        float sX = -mFrom.right;
+
+        float d = mUX * mUX + 2 * sX * MAX_ACCELERATION;
+        if (d >= 0) {
+            // sX can be reached under the MAX_ACCELERATION. Use MAX_ACCELERATION for x direction.
+            mAX = MAX_ACCELERATION;
+        } else {
+            // sX is not reachable, decrease the acceleration so that sX is almost reached.
+            d = 0;
+            mAX = mUX * mUX / (2 * -sX);
+        }
+        double t = (-mUX - Math.sqrt(d)) / mAX;
+
+        float sY = -mFrom.exactCenterY() + mIconRect.exactCenterY();
+
+        // Find vertical acceleration such that: u*t + a*t*t/2 = s
+        mAY = (float) ((sY - t * mUY) * 2 / (t * t));
+        return (int) Math.round(t);
+    }
+
     public final int getDuration() {
         return mDuration + DRAG_END_DELAY;
     }
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index a5498f7..163c953 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -22,7 +22,10 @@
 import android.view.ViewGroup;
 
 import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.config.FeatureFlags;
 
 import java.util.Arrays;
 
@@ -190,15 +193,17 @@
      * in portrait orientation. In landscape, [(icon + hotseat) column count x (icon row count)]
      */
     // TODO: get rid of the dynamic matrix creation
-    public static int[][] createSparseMatrixWithHotseat(CellLayout iconLayout,
-            CellLayout hotseatLayout, boolean isHotseatHorizontal, int allappsiconRank) {
+    public static int[][] createSparseMatrixWithHotseat(
+            CellLayout iconLayout, CellLayout hotseatLayout, DeviceProfile dp) {
 
         ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
         ViewGroup hotseatParent = hotseatLayout.getShortcutsAndWidgets();
 
-        boolean moreIconsInHotseatThanWorkspace = isHotseatHorizontal ?
-                hotseatLayout.getCountX() > iconLayout.getCountX() :
-                hotseatLayout.getCountY() > iconLayout.getCountY();
+        boolean isHotseatHorizontal = !dp.isVerticalBarLayout();
+        boolean moreIconsInHotseatThanWorkspace = !FeatureFlags.NO_ALL_APPS_ICON &&
+                (isHotseatHorizontal
+                        ? hotseatLayout.getCountX() > iconLayout.getCountX()
+                        : hotseatLayout.getCountY() > iconLayout.getCountY());
 
         int m, n;
         if (isHotseatHorizontal) {
@@ -210,6 +215,7 @@
         }
         int[][] matrix = createFullMatrix(m, n);
         if (moreIconsInHotseatThanWorkspace) {
+            int allappsiconRank = dp.inv.getAllAppsButtonRank();
             if (isHotseatHorizontal) {
                 for (int j = 0; j < n; j++) {
                     matrix[allappsiconRank][j] = ALL_APPS_COLUMN;
@@ -229,6 +235,7 @@
             int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
             int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
             if (moreIconsInHotseatThanWorkspace) {
+                int allappsiconRank = dp.inv.getAllAppsButtonRank();
                 if (isHotseatHorizontal && cx >= allappsiconRank) {
                     // Add 1 to account for the All Apps button.
                     cx++;
diff --git a/src/com/android/launcher3/util/GridOccupancy.java b/src/com/android/launcher3/util/GridOccupancy.java
new file mode 100644
index 0000000..6a10b0a
--- /dev/null
+++ b/src/com/android/launcher3/util/GridOccupancy.java
@@ -0,0 +1,101 @@
+package com.android.launcher3.util;
+
+import android.graphics.Rect;
+
+import com.android.launcher3.ItemInfo;
+
+/**
+ * Utility object to manage the occupancy in a grid.
+ */
+public class GridOccupancy {
+
+    private final int mCountX;
+    private final int mCountY;
+
+    public final boolean[][] cells;
+
+    public GridOccupancy(int countX, int countY) {
+        mCountX = countX;
+        mCountY = countY;
+        cells = new boolean[countX][countY];
+    }
+
+    /**
+     * Find the first vacant cell, if there is one.
+     *
+     * @param vacantOut Holds the x and y coordinate of the vacant cell
+     * @param spanX Horizontal cell span.
+     * @param spanY Vertical cell span.
+     *
+     * @return true if a vacant cell was found
+     */
+    public boolean findVacantCell(int[] vacantOut, int spanX, int spanY) {
+        for (int y = 0; (y + spanY) <= mCountY; y++) {
+            for (int x = 0; (x + spanX) <= mCountX; x++) {
+                boolean available = !cells[x][y];
+                out:
+                for (int i = x; i < x + spanX; i++) {
+                    for (int j = y; j < y + spanY; j++) {
+                        available = available && !cells[i][j];
+                        if (!available) break out;
+                    }
+                }
+                if (available) {
+                    vacantOut[0] = x;
+                    vacantOut[1] = y;
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public void copyTo(GridOccupancy dest) {
+        for (int i = 0; i < mCountX; i++) {
+            for (int j = 0; j < mCountY; j++) {
+                dest.cells[i][j] = cells[i][j];
+            }
+        }
+    }
+
+    public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
+        int x2 = x + spanX - 1;
+        int y2 = y + spanY - 1;
+        if (x < 0 || y < 0 || x2 >= mCountX || y2 >= mCountY) {
+            return false;
+        }
+        for (int i = x; i <= x2; i++) {
+            for (int j = y; j <= y2; j++) {
+                if (cells[i][j]) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    public void markCells(int cellX, int cellY, int spanX, int spanY, boolean value) {
+        if (cellX < 0 || cellY < 0) return;
+        for (int x = cellX; x < cellX + spanX && x < mCountX; x++) {
+            for (int y = cellY; y < cellY + spanY && y < mCountY; y++) {
+                cells[x][y] = value;
+            }
+        }
+    }
+
+    public void markCells(Rect r, boolean value) {
+        markCells(r.left, r.top, r.width(), r.height(), value);
+    }
+
+    public void markCells(CellAndSpan cell, boolean value) {
+        markCells(cell.cellX, cell.cellY, cell.spanX, cell.spanY, value);
+    }
+
+    public void markCells(ItemInfo item, boolean value) {
+        markCells(item.cellX, item.cellY, item.spanX, item.spanY, value);
+    }
+
+    public void clear() {
+        markCells(0, 0, mCountX, mCountY, false);
+    }
+}
diff --git a/src/com/android/launcher3/util/IconNormalizer.java b/src/com/android/launcher3/util/IconNormalizer.java
index 001cac0..040a1b5 100644
--- a/src/com/android/launcher3/util/IconNormalizer.java
+++ b/src/com/android/launcher3/util/IconNormalizer.java
@@ -19,6 +19,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
 
 import com.android.launcher3.LauncherAppState;
@@ -28,7 +29,7 @@
 public class IconNormalizer {
 
     // Ratio of icon visible area to full icon size for a square shaped icon
-    private static final float MAX_SQUARE_AREA_FACTOR = 359.0f / 576;
+    private static final float MAX_SQUARE_AREA_FACTOR = 375.0f / 576;
     // Ratio of icon visible area to full icon size for a circular shaped icon
     private static final float MAX_CIRCLE_AREA_FACTOR = 380.0f / 576;
 
@@ -74,8 +75,10 @@
      *
      * This closeness is used to determine the ratio of hull area to the full icon size.
      * Refer {@link #MAX_CIRCLE_AREA_FACTOR} and {@link #MAX_SQUARE_AREA_FACTOR}
+     *
+     * @param outBounds optional rect to receive the fraction distance from each edge.
      */
-    public synchronized float getScale(Drawable d) {
+    public synchronized float getScale(Drawable d, RectF outBounds) {
         int width = d.getIntrinsicWidth();
         int height = d.getIntrinsicHeight();
         if (width <= 0 || height <= 0) {
@@ -168,6 +171,14 @@
             scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1  - hullByRect);
         }
 
+        if (outBounds != null) {
+            outBounds.left = ((float) leftX) / width;
+            outBounds.right = 1 - ((float) rightX) / width;
+
+            outBounds.top = ((float) topY) / height;
+            outBounds.bottom = 1 - ((float) bottomY) / height;
+        }
+
         float areaScale = area / (width * height);
         // Use sqrt of the final ratio as the images is scaled across both width and height.
         float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1;
diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java
new file mode 100644
index 0000000..46e9184
--- /dev/null
+++ b/src/com/android/launcher3/util/ItemInfoMatcher.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.content.ComponentName;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.shortcuts.ShortcutKey;
+
+import java.util.HashSet;
+
+/**
+ * A utility class to check for {@link ItemInfo}
+ */
+public abstract class ItemInfoMatcher {
+
+    public abstract boolean matches(ItemInfo info, ComponentName cn);
+
+    public static ItemInfoMatcher ofComponents(
+            final HashSet<ComponentName> components, final UserHandleCompat user) {
+        return new ItemInfoMatcher() {
+            @Override
+            public boolean matches(ItemInfo info, ComponentName cn) {
+                return components.contains(cn) && info.user.equals(user);
+            }
+        };
+    }
+
+    public static ItemInfoMatcher ofPackages(
+            final HashSet<String> packageNames, final UserHandleCompat user) {
+        return new ItemInfoMatcher() {
+            @Override
+            public boolean matches(ItemInfo info, ComponentName cn) {
+                return packageNames.contains(cn.getPackageName()) && info.user.equals(user);
+            }
+        };
+    }
+
+    public static ItemInfoMatcher ofShortcutKeys(final HashSet<ShortcutKey> keys) {
+        return new ItemInfoMatcher() {
+            @Override
+            public boolean matches(ItemInfo info, ComponentName cn) {
+                return info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT &&
+                        keys.contains(ShortcutKey.fromShortcutInfo((ShortcutInfo) info));
+            }
+        };
+    }
+}
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index fb9bbb2..6661429 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -16,14 +16,8 @@
 
 package com.android.launcher3.util;
 
-import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Build;
-import android.util.Log;
 
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.ItemInfo;
@@ -35,27 +29,20 @@
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
-import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.shortcuts.ShortcutInfoCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 /**
  * Handles addition of app shortcuts for managed profiles.
  * Methods of class should only be called on {@link LauncherModel#sWorkerThread}.
  */
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class ManagedProfileHeuristic {
 
-    private static final String TAG = "ManagedProfileHeuristic";
-
     /**
      * Maintain a set of packages installed per user.
      */
@@ -76,228 +63,143 @@
     }
 
     private final Context mContext;
-    private final UserHandleCompat mUser;
     private final LauncherModel mModel;
-
-    private final SharedPreferences mPrefs;
-    private final long mUserSerial;
-    private final long mUserCreationTime;
-    private final String mPackageSetKey;
-
-    private ArrayList<ShortcutInfo> mHomescreenApps;
-    private ArrayList<ShortcutInfo> mWorkFolderApps;
-    private HashMap<ShortcutInfo, Long> mShortcutToInstallTimeMap;
+    private final UserHandleCompat mUser;
 
     private ManagedProfileHeuristic(Context context, UserHandleCompat user) {
         mContext = context;
         mUser = user;
         mModel = LauncherAppState.getInstance().getModel();
-
-        UserManagerCompat userManager = UserManagerCompat.getInstance(context);
-        mUserSerial = userManager.getSerialNumberForUser(user);
-        mUserCreationTime = userManager.getUserCreationTime(user);
-        mPackageSetKey = INSTALLED_PACKAGES_PREFIX + mUserSerial;
-
-        mPrefs = mContext.getSharedPreferences(LauncherFiles.MANAGED_USER_PREFERENCES_KEY,
-                Context.MODE_PRIVATE);
     }
 
-    private void initVars() {
-        mHomescreenApps = new ArrayList<>();
-        mWorkFolderApps = new ArrayList<>();
-        mShortcutToInstallTimeMap = new HashMap<>();
+    public void processPackageRemoved(String[] packages) {
+        Preconditions.assertWorkerThread();
+        ManagedProfilePackageHandler handler = new ManagedProfilePackageHandler();
+        for (String pkg : packages) {
+            handler.onPackageRemoved(pkg, mUser);
+        }
     }
 
-    /**
-     * Checks the list of user apps and adds icons for newly installed apps on the homescreen or
-     * workfolder.
-     */
+    public void processPackageAdd(String[] packages) {
+        Preconditions.assertWorkerThread();
+        ManagedProfilePackageHandler handler = new ManagedProfilePackageHandler();
+        for (String pkg : packages) {
+            handler.onPackageAdded(pkg, mUser);
+        }
+    }
+
     public void processUserApps(List<LauncherActivityInfoCompat> apps) {
-        initVars();
+        Preconditions.assertWorkerThread();
+        new ManagedProfilePackageHandler().processUserApps(apps, mUser);
+    }
 
-        HashSet<String> packageSet = new HashSet<>();
-        final boolean userAppsExisted = getUserApps(packageSet);
+    private class ManagedProfilePackageHandler extends CachedPackageTracker {
 
-        boolean newPackageAdded = false;
-        for (LauncherActivityInfoCompat info : apps) {
-            String packageName = info.getComponentName().getPackageName();
-            if (!packageSet.contains(packageName)) {
-                packageSet.add(packageName);
-                newPackageAdded = true;
-                markForAddition(info, info.getFirstInstallTime());
-            }
+        private ManagedProfilePackageHandler() {
+            super(mContext, LauncherFiles.MANAGED_USER_PREFERENCES_KEY);
         }
 
-        if (newPackageAdded) {
-            mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply();
+        protected void onLauncherAppsAdded(
+                List<LauncherActivityInstallInfo> apps, UserHandleCompat user, boolean userAppsExisted) {
+            ArrayList<ShortcutInfo> workFolderApps = new ArrayList<>();
+            ArrayList<ShortcutInfo> homescreenApps = new ArrayList<>();
+
+            int count = apps.size();
+            long folderCreationTime =
+                    mUserManager.getUserCreationTime(user) + AUTO_ADD_TO_FOLDER_DURATION;
+
+            for (int i = 0; i < count; i++) {
+                LauncherActivityInstallInfo info = apps.get(i);
+
+                ShortcutInfo si = new ShortcutInfo(info.info, mContext);
+                ((info.installTime <= folderCreationTime) ? workFolderApps : homescreenApps).add(si);
+            }
+
+            finalizeWorkFolder(user, workFolderApps, homescreenApps);
+
             // Do not add shortcuts on the homescreen for the first time. This prevents the launcher
             // getting filled with the managed user apps, when it start with a fresh DB (or after
             // a very long time).
-            finalizeAdditions(userAppsExisted);
-        }
-    }
-
-    private void markForAddition(LauncherActivityInfoCompat info, long installTime) {
-        ArrayList<ShortcutInfo> targetList =
-                (installTime <= mUserCreationTime + AUTO_ADD_TO_FOLDER_DURATION) ?
-                        mWorkFolderApps : mHomescreenApps;
-        ShortcutInfo si = ShortcutInfo.fromActivityInfo(info, mContext);
-        mShortcutToInstallTimeMap.put(si, installTime);
-        targetList.add(si);
-    }
-
-    private void sortList(ArrayList<ShortcutInfo> infos) {
-        Collections.sort(infos, new Comparator<ShortcutInfo>() {
-
-            @Override
-            public int compare(ShortcutInfo lhs, ShortcutInfo rhs) {
-                Long lhsTime = mShortcutToInstallTimeMap.get(lhs);
-                Long rhsTime = mShortcutToInstallTimeMap.get(rhs);
-                return Utilities.longCompare(lhsTime == null ? 0 : lhsTime,
-                        rhsTime == null ? 0 : rhsTime);
+            if (userAppsExisted && !homescreenApps.isEmpty()) {
+                mModel.addAndBindAddedWorkspaceItems(mContext, homescreenApps);
             }
-        });
-    }
-
-    /**
-     * Adds and binds shortcuts marked to be added to the work folder.
-     */
-    private void finalizeWorkFolder() {
-        if (mWorkFolderApps.isEmpty()) {
-            return;
         }
-        sortList(mWorkFolderApps);
 
-        // Try to get a work folder.
-        String folderIdKey = USER_FOLDER_ID_PREFIX + mUserSerial;
-        if (mPrefs.contains(folderIdKey)) {
-            long folderId = mPrefs.getLong(folderIdKey, 0);
-            final FolderInfo workFolder = mModel.findFolderById(folderId);
+        @Override
+        protected void onLauncherPackageRemoved(String packageName, UserHandleCompat user) {
+        }
 
-            if (workFolder == null || !workFolder.hasOption(FolderInfo.FLAG_WORK_FOLDER)) {
-                // Could not get a work folder. Add all the icons to homescreen.
-                mHomescreenApps.addAll(mWorkFolderApps);
+        /**
+         * Adds and binds shortcuts marked to be added to the work folder.
+         */
+        private void finalizeWorkFolder(
+                UserHandleCompat user, final ArrayList<ShortcutInfo> workFolderApps,
+                ArrayList<ShortcutInfo> homescreenApps) {
+            if (workFolderApps.isEmpty()) {
                 return;
             }
-            saveWorkFolderShortcuts(folderId, workFolder.contents.size());
+            // Try to get a work folder.
+            String folderIdKey = USER_FOLDER_ID_PREFIX + mUserManager.getSerialNumberForUser(user);
+            if (mPrefs.contains(folderIdKey)) {
+                long folderId = mPrefs.getLong(folderIdKey, 0);
+                final FolderInfo workFolder = mModel.findFolderById(folderId);
 
-            final ArrayList<ShortcutInfo> shortcuts = mWorkFolderApps;
-            // FolderInfo could already be bound. We need to add shortcuts on the UI thread.
-            new MainThreadExecutor().execute(new Runnable() {
-
-                @Override
-                public void run() {
-                    for (ShortcutInfo info : shortcuts) {
-                        workFolder.add(info);
-                    }
+                if (workFolder == null || !workFolder.hasOption(FolderInfo.FLAG_WORK_FOLDER)) {
+                    // Could not get a work folder. Add all the icons to homescreen.
+                    homescreenApps.addAll(0, workFolderApps);
+                    return;
                 }
-            });
-        } else {
-            // Create a new folder.
-            final FolderInfo workFolder = new FolderInfo();
-            workFolder.title = mContext.getText(R.string.work_folder_name);
-            workFolder.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null);
+                saveWorkFolderShortcuts(folderId, workFolder.contents.size(), workFolderApps);
 
-            // Add all shortcuts before adding it to the UI, as an empty folder might get deleted.
-            for (ShortcutInfo info : mWorkFolderApps) {
-                workFolder.add(info);
+                // FolderInfo could already be bound. We need to add shortcuts on the UI thread.
+                new MainThreadExecutor().execute(new Runnable() {
+
+                    @Override
+                    public void run() {
+                        for (ShortcutInfo info : workFolderApps) {
+                            workFolder.add(info, false);
+                        }
+                    }
+                });
+            } else {
+                // Create a new folder.
+                final FolderInfo workFolder = new FolderInfo();
+                workFolder.title = mContext.getText(R.string.work_folder_name);
+                workFolder.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null);
+
+                // Add all shortcuts before adding it to the UI, as an empty folder might get deleted.
+                for (ShortcutInfo info : workFolderApps) {
+                    workFolder.add(info, false);
+                }
+
+                // Add the item to home screen and DB. This also generates an item id synchronously.
+                ArrayList<ItemInfo> itemList = new ArrayList<ItemInfo>(1);
+                itemList.add(workFolder);
+                mModel.addAndBindAddedWorkspaceItems(mContext, itemList);
+                mPrefs.edit().putLong(folderIdKey, workFolder.id).apply();
+
+                saveWorkFolderShortcuts(workFolder.id, 0, workFolderApps);
             }
+        }
 
-            // Add the item to home screen and DB. This also generates an item id synchronously.
-            ArrayList<ItemInfo> itemList = new ArrayList<ItemInfo>(1);
-            itemList.add(workFolder);
-            mModel.addAndBindAddedWorkspaceItems(mContext, itemList);
-            mPrefs.edit().putLong(USER_FOLDER_ID_PREFIX + mUserSerial, workFolder.id).apply();
-
-            saveWorkFolderShortcuts(workFolder.id, 0);
+        @Override
+        public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts,
+                UserHandleCompat user) {
+            // Do nothing
         }
     }
 
     /**
      * Add work folder shortcuts to the DB.
      */
-    private void saveWorkFolderShortcuts(long workFolderId, int startingRank) {
-        for (ItemInfo info : mWorkFolderApps) {
+    private void saveWorkFolderShortcuts(
+            long workFolderId, int startingRank, ArrayList<ShortcutInfo> workFolderApps) {
+        for (ItemInfo info : workFolderApps) {
             info.rank = startingRank++;
             LauncherModel.addItemToDatabase(mContext, info, workFolderId, 0, 0, 0);
         }
     }
 
-    /**
-     * Adds and binds all shortcuts marked for addition.
-     */
-    private void finalizeAdditions(boolean addHomeScreenShortcuts) {
-        finalizeWorkFolder();
-
-        if (addHomeScreenShortcuts && !mHomescreenApps.isEmpty()) {
-            sortList(mHomescreenApps);
-            mModel.addAndBindAddedWorkspaceItems(mContext, mHomescreenApps);
-        }
-    }
-
-    /**
-     * Updates the list of installed apps and adds any new icons on homescreen or work folder.
-     */
-    public void processPackageAdd(String[] packages) {
-        initVars();
-        HashSet<String> packageSet = new HashSet<>();
-        final boolean userAppsExisted = getUserApps(packageSet);
-
-        boolean newPackageAdded = false;
-        long installTime = System.currentTimeMillis();
-        LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
-
-        for (String packageName : packages) {
-            if (!packageSet.contains(packageName)) {
-                packageSet.add(packageName);
-                newPackageAdded = true;
-
-                List<LauncherActivityInfoCompat> activities =
-                        launcherApps.getActivityList(packageName, mUser);
-                if (!activities.isEmpty()) {
-                    markForAddition(activities.get(0), installTime);
-                }
-            }
-        }
-
-        if (newPackageAdded) {
-            mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply();
-            finalizeAdditions(userAppsExisted);
-        }
-    }
-
-    /**
-     * Updates the list of installed packages for the user.
-     */
-    public void processPackageRemoved(String[] packages) {
-        HashSet<String> packageSet = new HashSet<String>();
-        getUserApps(packageSet);
-        boolean packageRemoved = false;
-
-        for (String packageName : packages) {
-            if (packageSet.remove(packageName)) {
-                packageRemoved = true;
-            }
-        }
-
-        if (packageRemoved) {
-            mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply();
-        }
-    }
-
-    /**
-     * Reads the list of user apps which have already been processed.
-     * @return false if the list didn't exist, true otherwise
-     */
-    private boolean getUserApps(HashSet<String> outExistingApps) {
-        Set<String> userApps = mPrefs.getStringSet(mPackageSetKey, null);
-        if (userApps == null) {
-            return false;
-        } else {
-            outExistingApps.addAll(userApps);
-            return true;
-        }
-    }
 
     /**
      * Verifies that entries corresponding to {@param users} exist and removes all invalid entries.
diff --git a/src/com/android/launcher3/util/MultiHashMap.java b/src/com/android/launcher3/util/MultiHashMap.java
new file mode 100644
index 0000000..b7275c1
--- /dev/null
+++ b/src/com/android/launcher3/util/MultiHashMap.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * A utility map from keys to an ArrayList of values.
+ */
+public class MultiHashMap<K, V> extends HashMap<K, ArrayList<V>> {
+
+    public MultiHashMap() { }
+
+    public MultiHashMap(int size) {
+        super(size);
+    }
+
+    public void addToList(K key, V value) {
+        ArrayList<V> list = get(key);
+        if (list == null) {
+            list = new ArrayList<>();
+            list.add(value);
+            put(key, list);
+        } else {
+            list.add(value);
+        }
+    }
+
+    @Override
+    public MultiHashMap<K, V> clone() {
+        MultiHashMap<K, V> map = new MultiHashMap<>(size());
+        for (Entry<K, ArrayList<V>> entry : entrySet()) {
+            map.put(entry.getKey(), new ArrayList<V>(entry.getValue()));
+        }
+        return map;
+    }
+}
diff --git a/src/com/android/launcher3/util/MultiStateAlphaController.java b/src/com/android/launcher3/util/MultiStateAlphaController.java
new file mode 100644
index 0000000..956fc9e
--- /dev/null
+++ b/src/com/android/launcher3/util/MultiStateAlphaController.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.content.Context;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+
+import java.util.Arrays;
+
+/**
+ * A utility class which divides the alpha for a view across multiple states.
+ */
+public class MultiStateAlphaController {
+
+    private final View mTargetView;
+    private final float[] mAlphas;
+    private final AccessibilityManager mAm;
+    private int mZeroAlphaListenerCount = 0;
+
+    public MultiStateAlphaController(View view, int stateCount) {
+        mTargetView = view;
+        mAlphas = new float[stateCount];
+        Arrays.fill(mAlphas, 1);
+
+        mAm = (AccessibilityManager) view.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+    }
+
+    public void setAlphaAtIndex(float alpha, int index) {
+        mAlphas[index] = alpha;
+        updateAlpha();
+    }
+
+    private void updateAlpha() {
+        // Only update the alpha if no zero-alpha animation is running.
+        if (mZeroAlphaListenerCount > 0) {
+            return;
+        }
+        float finalAlpha = 1;
+        for (float a : mAlphas) {
+            finalAlpha = finalAlpha * a;
+        }
+        mTargetView.setAlpha(finalAlpha);
+        mTargetView.setVisibility(finalAlpha > 0 ? View.VISIBLE
+                : (mAm.isEnabled() ? View.GONE : View.INVISIBLE));
+    }
+
+    /**
+     * Returns an animator which changes the alpha at the index {@param index}
+     * to {@param finalAlpha}. Alphas at other index are not affected.
+     */
+    public Animator animateAlphaAtIndex(float finalAlpha, final int index) {
+        final ValueAnimator anim;
+
+        if (Float.compare(finalAlpha, mAlphas[index]) == 0) {
+            // Return a dummy animator to avoid null checks.
+            anim = ValueAnimator.ofFloat(0, 0);
+        } else {
+            ValueAnimator animator = ValueAnimator.ofFloat(mAlphas[index], finalAlpha);
+            animator.addUpdateListener(new AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                    float value = (Float) valueAnimator.getAnimatedValue();
+                    setAlphaAtIndex(value, index);
+                }
+            });
+            anim = animator;
+        }
+
+        if (Float.compare(finalAlpha, 0f) == 0) {
+            // In case when any channel is animating to 0, and the current alpha is also 0, do not
+            // update alpha of the target view while the animation is running.
+            // We special case '0' because if any channel is set to 0, values of other
+            // channels do not matter.
+            anim.addListener(new ZeroAlphaAnimatorListener());
+        }
+        return anim;
+    }
+
+    private class ZeroAlphaAnimatorListener extends AnimatorListenerAdapter {
+
+        private boolean mStartedAtZero = false;
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            mStartedAtZero = Float.compare(mTargetView.getAlpha(), 0f) == 0;
+            if (mStartedAtZero) {
+                mZeroAlphaListenerCount++;
+                mTargetView.setAlpha(0);
+            }
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            if (mStartedAtZero) {
+                mZeroAlphaListenerCount--;
+                updateAlpha();
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/NoLocaleSqliteContext.java b/src/com/android/launcher3/util/NoLocaleSqliteContext.java
new file mode 100644
index 0000000..3b258e4
--- /dev/null
+++ b/src/com/android/launcher3/util/NoLocaleSqliteContext.java
@@ -0,0 +1,27 @@
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.database.DatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+
+/**
+ * A context wrapper which creates databases without support for localized collators.
+ */
+public class NoLocaleSqliteContext extends ContextWrapper {
+
+    // TODO: Use the flag defined in Context when the new SDK is available
+    private static final int MODE_NO_LOCALIZED_COLLATORS = 0x0010;
+
+    public NoLocaleSqliteContext(Context context) {
+        super(context);
+    }
+
+    @Override
+    public SQLiteDatabase openOrCreateDatabase(
+            String name, int mode, CursorFactory factory, DatabaseErrorHandler errorHandler) {
+        return super.openOrCreateDatabase(
+                name, mode | MODE_NO_LOCALIZED_COLLATORS, factory, errorHandler);
+    }
+}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 08e8e86..3e15d05 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -16,17 +16,27 @@
 
 package com.android.launcher3.util;
 
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.Intent;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.os.Build;
+import android.text.TextUtils;
 
 import com.android.launcher3.Utilities;
 
+import java.util.ArrayList;
+
 /**
  * Utility methods using package manager
  */
 public class PackageManagerHelper {
 
     private static final int FLAG_SUSPENDED = 1<<30;
+    private static final String LIVE_WALLPAPER_PICKER_PKG = "com.android.wallpaper.livepicker";
 
     /**
      * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't
@@ -68,4 +78,76 @@
             return false;
         }
     }
+
+    /**
+     * Returns the package for a wallpaper picker system app giving preference to a app which
+     * is not as image picker.
+     */
+    public static String getWallpaperPickerPackage(PackageManager pm) {
+        ArrayList<String> excludePackages = new ArrayList<>();
+        // Exclude packages which contain an image picker
+        for (ResolveInfo info : pm.queryIntentActivities(
+                new Intent(Intent.ACTION_GET_CONTENT).setType("image/*"), 0)) {
+            excludePackages.add(info.activityInfo.packageName);
+        }
+        excludePackages.add(LIVE_WALLPAPER_PICKER_PKG);
+
+        for (ResolveInfo info : pm.queryIntentActivities(
+                new Intent(Intent.ACTION_SET_WALLPAPER), 0)) {
+            if (!excludePackages.contains(info.activityInfo.packageName) &&
+                    (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                return info.activityInfo.packageName;
+            }
+        }
+        return excludePackages.get(0);
+    }
+
+    /**
+     * Returns true if {@param srcPackage} has the permission required to start the activity from
+     * {@param intent}. If {@param srcPackage} is null, then the activity should not need
+     * any permissions
+     */
+    public static boolean hasPermissionForActivity(Context context, Intent intent,
+            String srcPackage) {
+        PackageManager pm = context.getPackageManager();
+        ResolveInfo target = pm.resolveActivity(intent, 0);
+        if (target == null) {
+            // Not a valid target
+            return false;
+        }
+        if (TextUtils.isEmpty(target.activityInfo.permission)) {
+            // No permission is needed
+            return true;
+        }
+        if (TextUtils.isEmpty(srcPackage)) {
+            // The activity requires some permission but there is no source.
+            return false;
+        }
+
+        // Source does not have sufficient permissions.
+        if(pm.checkPermission(target.activityInfo.permission, srcPackage) !=
+                PackageManager.PERMISSION_GRANTED) {
+            return false;
+        }
+
+        if (!Utilities.ATLEAST_MARSHMALLOW) {
+            // These checks are sufficient for below M devices.
+            return true;
+        }
+
+        // On M and above also check AppOpsManager for compatibility mode permissions.
+        if (TextUtils.isEmpty(AppOpsManager.permissionToOp(target.activityInfo.permission))) {
+            // There is no app-op for this permission, which could have been disabled.
+            return true;
+        }
+
+        // There is no direct way to check if the app-op is allowed for a particular app. Since
+        // app-op is only enabled for apps running in compatibility mode, simply block such apps.
+
+        try {
+            return pm.getApplicationInfo(srcPackage, 0).targetSdkVersion >= Build.VERSION_CODES.M;
+        } catch (NameNotFoundException e) { }
+
+        return false;
+    }
 }
diff --git a/src/com/android/launcher3/util/PendingRequestArgs.java b/src/com/android/launcher3/util/PendingRequestArgs.java
new file mode 100644
index 0000000..bade967
--- /dev/null
+++ b/src/com/android/launcher3/util/PendingRequestArgs.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.content.ContentValues;
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+
+/**
+ * Utility class to store information regarding a pending request made by launcher. This information
+ * can be saved across launcher instances.
+ */
+public class PendingRequestArgs extends ItemInfo implements Parcelable {
+
+    private static final int TYPE_NONE = 0;
+    private static final int TYPE_INTENT = 1;
+    private static final int TYPE_APP_WIDGET = 2;
+
+    private final int mArg1;
+    private final int mObjectType;
+    private final Parcelable mObject;
+
+    public PendingRequestArgs(ItemInfo info) {
+        mArg1 = 0;
+        mObjectType = TYPE_NONE;
+        mObject = null;
+
+        copyFrom(info);
+    }
+
+    private PendingRequestArgs(int arg1, int objectType, Parcelable object) {
+        mArg1 = arg1;
+        mObjectType = objectType;
+        mObject = object;
+    }
+
+    public PendingRequestArgs(Parcel parcel) {
+        readFromValues(ContentValues.CREATOR.createFromParcel(parcel));
+
+        mArg1 = parcel.readInt();
+        mObjectType = parcel.readInt();
+        if (parcel.readInt() != 0) {
+            mObject = mObjectType == TYPE_INTENT
+                    ? Intent.CREATOR.createFromParcel(parcel)
+                    : new LauncherAppWidgetProviderInfo(parcel);
+        } else {
+            mObject = null;
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        ContentValues itemValues = new ContentValues();
+        writeToValues(itemValues);
+        itemValues.writeToParcel(dest, flags);
+
+        dest.writeInt(mArg1);
+        dest.writeInt(mObjectType);
+        if (mObject != null) {
+            dest.writeInt(1);
+            mObject.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
+    }
+
+    public LauncherAppWidgetProviderInfo getWidgetProvider() {
+        return mObjectType == TYPE_APP_WIDGET ? (LauncherAppWidgetProviderInfo) mObject : null;
+    }
+
+    public int getWidgetId() {
+        return mObjectType == TYPE_APP_WIDGET ? mArg1 : 0;
+    }
+
+    public Intent getPendingIntent() {
+        return mObjectType == TYPE_INTENT ? (Intent) mObject : null;
+    }
+
+    public int getRequestCode() {
+        return mObjectType == TYPE_INTENT ? mArg1 : 0;
+    }
+
+    public static PendingRequestArgs forWidgetInfo(
+            int appWidgetId, LauncherAppWidgetProviderInfo widgetInfo, ItemInfo info) {
+        PendingRequestArgs args = new PendingRequestArgs(appWidgetId, TYPE_APP_WIDGET, widgetInfo);
+        args.copyFrom(info);
+        return args;
+    }
+
+    public static PendingRequestArgs forIntent(int requestCode, Intent intent, ItemInfo info) {
+        PendingRequestArgs args = new PendingRequestArgs(requestCode, TYPE_INTENT, intent);
+        args.copyFrom(info);
+        return args;
+    }
+
+    public static final Parcelable.Creator<PendingRequestArgs> CREATOR =
+            new Parcelable.Creator<PendingRequestArgs>() {
+                public PendingRequestArgs createFromParcel(Parcel source) {
+                    return new PendingRequestArgs(source);
+                }
+
+                public PendingRequestArgs[] newArray(int size) {
+                    return new PendingRequestArgs[size];
+                }
+            };
+}
diff --git a/src/com/android/launcher3/util/PillRevealOutlineProvider.java b/src/com/android/launcher3/util/PillRevealOutlineProvider.java
new file mode 100644
index 0000000..3f1e11a
--- /dev/null
+++ b/src/com/android/launcher3/util/PillRevealOutlineProvider.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.annotation.TargetApi;
+import android.graphics.Rect;
+import android.os.Build;
+import android.view.ViewOutlineProvider;
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+/**
+ * A {@link ViewOutlineProvider} that animates a reveal in a "pill" shape.
+ * A pill is simply a round rect, but we assume the width is greater than
+ * the height and that the radius is equal to half the height.
+ */
+public class PillRevealOutlineProvider extends RevealOutlineAnimation {
+
+    private int mCenterX;
+    private int mCenterY;
+    protected Rect mPillRect;
+
+    /**
+     * @param x reveal center x
+     * @param y reveal center y
+     * @param pillRect round rect that represents the final pill shape
+     */
+    public PillRevealOutlineProvider(int x, int y, Rect pillRect) {
+        mCenterX = x;
+        mCenterY = y;
+        mPillRect = pillRect;
+        mOutlineRadius = pillRect.height() / 2f;
+    }
+
+    @Override
+    public boolean shouldRemoveElevationDuringAnimation() {
+        return false;
+    }
+
+    @Override
+    public void setProgress(float progress) {
+        // Assumes width is greater than height.
+        int centerToEdge = Math.max(mCenterX, mPillRect.width() - mCenterX);
+        int currentSize = (int) (progress * centerToEdge);
+
+        // Bound the outline to the final pill shape defined by mPillRect.
+        mOutline.left = Math.max(mPillRect.left, mCenterX - currentSize);
+        mOutline.top = Math.max(mPillRect.top, mCenterY - currentSize);
+        mOutline.right = Math.min(mPillRect.right, mCenterX + currentSize);
+        mOutline.bottom = Math.min(mPillRect.bottom, mCenterY + currentSize);
+        mOutlineRadius = mOutline.height() / 2;
+    }
+}
diff --git a/src/com/android/launcher3/util/PillWidthRevealOutlineProvider.java b/src/com/android/launcher3/util/PillWidthRevealOutlineProvider.java
new file mode 100644
index 0000000..89dda3b
--- /dev/null
+++ b/src/com/android/launcher3/util/PillWidthRevealOutlineProvider.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.graphics.Rect;
+
+/**
+ * Extension of {@link PillRevealOutlineProvider} which only changes the width of the pill.
+ */
+public class PillWidthRevealOutlineProvider extends PillRevealOutlineProvider {
+
+    private final int mStartLeft;
+    private final int mStartRight;
+
+    public PillWidthRevealOutlineProvider(Rect pillRect, int left, int right) {
+        super(0, 0, pillRect);
+        mOutline.set(pillRect);
+        mStartLeft = left;
+        mStartRight = right;
+    }
+
+    @Override
+    public void setProgress(float progress) {
+        mOutline.left = (int) (progress * mPillRect.left + (1 - progress) * mStartLeft);
+        mOutline.right = (int) (progress * mPillRect.right + (1 - progress) * mStartRight);
+    }
+}
diff --git a/src/com/android/launcher3/util/Preconditions.java b/src/com/android/launcher3/util/Preconditions.java
new file mode 100644
index 0000000..89353e1
--- /dev/null
+++ b/src/com/android/launcher3/util/Preconditions.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.os.Looper;
+
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.config.ProviderConfig;
+
+/**
+ * A set of utility methods for thread verification.
+ */
+public class Preconditions {
+
+    public static void assertNotNull(Object o) {
+        if (ProviderConfig.IS_DOGFOOD_BUILD && o == null) {
+            throw new IllegalStateException();
+        }
+    }
+
+    public static void assertWorkerThread() {
+        if (ProviderConfig.IS_DOGFOOD_BUILD && !isSameLooper(LauncherModel.getWorkerLooper())) {
+            throw new IllegalStateException();
+        }
+    }
+
+    public static void assertUIThread() {
+        if (ProviderConfig.IS_DOGFOOD_BUILD && !isSameLooper(Looper.getMainLooper())) {
+            throw new IllegalStateException();
+        }
+    }
+
+    public static void assertNonUiThread() {
+        if (ProviderConfig.IS_DOGFOOD_BUILD && isSameLooper(Looper.getMainLooper())) {
+            throw new IllegalStateException();
+        }
+    }
+
+    private static boolean isSameLooper(Looper looper) {
+        return Looper.myLooper() == looper;
+    }
+}
diff --git a/src/com/android/launcher3/util/RevealOutlineAnimation.java b/src/com/android/launcher3/util/RevealOutlineAnimation.java
new file mode 100644
index 0000000..4560477
--- /dev/null
+++ b/src/com/android/launcher3/util/RevealOutlineAnimation.java
@@ -0,0 +1,86 @@
+package com.android.launcher3.util;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * A {@link ViewOutlineProvider} that has helper functions to create reveal animations.
+ * This class should be extended so that subclasses can define the reveal shape as the
+ * animation progresses from 0 to 1.
+ */
+public abstract class RevealOutlineAnimation extends ViewOutlineProvider {
+    protected Rect mOutline;
+    protected float mOutlineRadius;
+
+    public RevealOutlineAnimation() {
+        mOutline = new Rect();
+    }
+
+    /** Returns whether elevation should be removed for the duration of the reveal animation. */
+    abstract boolean shouldRemoveElevationDuringAnimation();
+    /** Sets the progress, from 0 to 1, of the reveal animation. */
+    abstract void setProgress(float progress);
+
+    public ValueAnimator createRevealAnimator(final View revealView) {
+        return createRevealAnimator(revealView, false);
+    }
+
+    public ValueAnimator createRevealAnimator(final View revealView, boolean isReversed) {
+        ValueAnimator va =
+                isReversed ? ValueAnimator.ofFloat(1f, 0f) : ValueAnimator.ofFloat(0f, 1f);
+        final float elevation = revealView.getElevation();
+
+        va.addListener(new AnimatorListenerAdapter() {
+            private boolean mWasCanceled = false;
+
+            public void onAnimationStart(Animator animation) {
+                revealView.setOutlineProvider(RevealOutlineAnimation.this);
+                revealView.setClipToOutline(true);
+                if (shouldRemoveElevationDuringAnimation()) {
+                    revealView.setTranslationZ(-elevation);
+                }
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mWasCanceled = true;
+            }
+
+            public void onAnimationEnd(Animator animation) {
+                if (!mWasCanceled) {
+                    revealView.setOutlineProvider(ViewOutlineProvider.BACKGROUND);
+                    revealView.setClipToOutline(false);
+                    if (shouldRemoveElevationDuringAnimation()) {
+                        revealView.setTranslationZ(0);
+                    }
+                }
+            }
+
+        });
+
+        va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator arg0) {
+                float progress = (Float) arg0.getAnimatedValue();
+                setProgress(progress);
+                revealView.invalidateOutline();
+                if (!Utilities.ATLEAST_LOLLIPOP_MR1) {
+                    revealView.invalidate();
+                }
+            }
+        });
+        return va;
+    }
+
+    @Override
+    public void getOutline(View v, Outline outline) {
+        outline.setRoundRect(mOutline, mOutlineRadius);
+    }
+}
diff --git a/src/com/android/launcher3/util/RevealOutlineProvider.java b/src/com/android/launcher3/util/RevealOutlineProvider.java
deleted file mode 100644
index 0db3984..0000000
--- a/src/com/android/launcher3/util/RevealOutlineProvider.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.android.launcher3.util;
-
-import android.annotation.TargetApi;
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.os.Build;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class RevealOutlineProvider extends ViewOutlineProvider {
-
-    private int mCenterX;
-    private int mCenterY;
-    private float mRadius0;
-    private float mRadius1;
-    private int mCurrentRadius;
-
-    private final Rect mOval;
-
-    /**
-     * @param x reveal center x
-     * @param y reveal center y
-     * @param r0 initial radius
-     * @param r1 final radius
-     */
-    public RevealOutlineProvider(int x, int y, float r0, float r1) {
-        mCenterX = x;
-        mCenterY = y;
-        mRadius0 = r0;
-        mRadius1 = r1;
-
-        mOval = new Rect();
-    }
-
-    public void setProgress(float progress) {
-        mCurrentRadius = (int) ((1 - progress) * mRadius0 + progress * mRadius1);
-
-        mOval.left = mCenterX - mCurrentRadius;
-        mOval.top = mCenterY - mCurrentRadius;
-        mOval.right = mCenterX + mCurrentRadius;
-        mOval.bottom = mCenterY + mCurrentRadius;
-    }
-
-    @Override
-    public void getOutline(View v, Outline outline) {
-        outline.setRoundRect(mOval, mCurrentRadius);
-    }
-}
diff --git a/src/com/android/launcher3/util/SQLiteCacheHelper.java b/src/com/android/launcher3/util/SQLiteCacheHelper.java
index 62a30d0..d1cfe42 100644
--- a/src/com/android/launcher3/util/SQLiteCacheHelper.java
+++ b/src/com/android/launcher3/util/SQLiteCacheHelper.java
@@ -29,22 +29,6 @@
     }
 
     /**
-     * @see SQLiteDatabase#update(String, ContentValues, String, String[])
-     */
-    public void update(ContentValues values, String whereClause, String[] whereArgs) {
-        if (mIgnoreWrites) {
-            return;
-        }
-        try {
-            mOpenHelper.getWritableDatabase().update(mTableName, values, whereClause, whereArgs);
-        } catch (SQLiteFullException e) {
-            onDiskFull(e);
-        } catch (SQLiteException e) {
-            Log.d(TAG, "Ignoring sqlite exception", e);
-        }
-    }
-
-    /**
      * @see SQLiteDatabase#delete(String, String, String[])
      */
     public void delete(String whereClause, String[] whereArgs) {
@@ -98,7 +82,7 @@
     private class MySQLiteOpenHelper extends SQLiteOpenHelper {
 
         public MySQLiteOpenHelper(Context context, String name, int version) {
-            super(context, name, null, version);
+            super(new NoLocaleSqliteContext(context), name, null, version);
         }
 
         @Override
diff --git a/src/com/android/launcher3/util/TouchController.java b/src/com/android/launcher3/util/TouchController.java
new file mode 100644
index 0000000..d1409c8
--- /dev/null
+++ b/src/com/android/launcher3/util/TouchController.java
@@ -0,0 +1,8 @@
+package com.android.launcher3.util;
+
+import android.view.MotionEvent;
+
+public interface TouchController {
+    boolean onTouchEvent(MotionEvent ev);
+    boolean onInterceptTouchEvent(MotionEvent ev);
+}
diff --git a/src/com/android/launcher3/util/TransformingTouchDelegate.java b/src/com/android/launcher3/util/TransformingTouchDelegate.java
new file mode 100644
index 0000000..3197ba9
--- /dev/null
+++ b/src/com/android/launcher3/util/TransformingTouchDelegate.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.MotionEvent;
+import android.view.TouchDelegate;
+import android.view.View;
+
+/**
+ * This class differs from the framework {@link TouchDelegate} in that it transforms the
+ * coordinates of the motion event to the provided bounds.
+ *
+ * You can also modify the bounds post construction. Since the bounds are available during layout,
+ * this avoids new object creation during every layout.
+ */
+public class TransformingTouchDelegate extends TouchDelegate {
+    private static final Rect sTempRect = new Rect();
+
+    private final RectF mBounds;
+
+    private final RectF mTouchCheckBounds;
+    private float mTouchExtension;
+    private boolean mWasTouchOutsideBounds;
+
+    private View mDelegateView;
+    private boolean mDelegateTargeted;
+
+    public TransformingTouchDelegate(View delegateView) {
+        super(sTempRect, delegateView);
+
+        mDelegateView = delegateView;
+        mBounds = new RectF();
+        mTouchCheckBounds = new RectF();
+    }
+
+    public void setBounds(int left, int top, int right, int bottom) {
+        mBounds.set(left, top, right, bottom);
+        updateTouchBounds();
+    }
+
+    public void extendTouchBounds(float extension) {
+        mTouchExtension = extension;
+        updateTouchBounds();
+    }
+
+    private void updateTouchBounds() {
+        mTouchCheckBounds.set(mBounds);
+        mTouchCheckBounds.inset(-mTouchExtension, -mTouchExtension);
+    }
+
+    public void setDelegateView(View view) {
+        mDelegateView = view;
+    }
+
+    /**
+     * Will forward touch events to the delegate view if the event is within the bounds
+     * specified in the constructor.
+     *
+     * @param event The touch event to forward
+     * @return True if the event was forwarded to the delegate, false otherwise.
+     */
+    public boolean onTouchEvent(MotionEvent event) {
+        boolean sendToDelegate = false;
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mDelegateTargeted = mTouchCheckBounds.contains(event.getX(), event.getY());
+                if (mDelegateTargeted) {
+                    mWasTouchOutsideBounds = !mBounds.contains(event.getX(), event.getY());
+                    sendToDelegate = true;
+                }
+                break;
+            case MotionEvent.ACTION_MOVE:
+                sendToDelegate = mDelegateTargeted;
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                sendToDelegate = mDelegateTargeted;
+                mDelegateTargeted = false;
+                break;
+        }
+        boolean handled = false;
+        if (sendToDelegate) {
+            float x = event.getX();
+            float y = event.getY();
+            if (mWasTouchOutsideBounds) {
+                event.setLocation(mBounds.centerX(), mBounds.centerY());
+            } else {
+                event.offsetLocation(-mBounds.left, -mBounds.top);
+            }
+            handled = mDelegateView.dispatchTouchEvent(event);
+            event.setLocation(x, y);
+        }
+        return handled;
+    }
+}
diff --git a/src/com/android/launcher3/util/UiThreadCircularReveal.java b/src/com/android/launcher3/util/UiThreadCircularReveal.java
deleted file mode 100644
index f2b5e5e..0000000
--- a/src/com/android/launcher3/util/UiThreadCircularReveal.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.android.launcher3.util;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.view.View;
-import android.view.ViewOutlineProvider;
-
-import com.android.launcher3.Utilities;
-
-@TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class UiThreadCircularReveal {
-
-    public static ValueAnimator createCircularReveal(View v, int x, int y, float r0, float r1) {
-        return createCircularReveal(v, x, y, r0, r1, ViewOutlineProvider.BACKGROUND);
-    }
-
-    public static ValueAnimator createCircularReveal(View v, int x, int y, float r0, float r1,
-            final ViewOutlineProvider originalProvider) {
-        ValueAnimator va = ValueAnimator.ofFloat(0f, 1f);
-
-        final View revealView = v;
-        final RevealOutlineProvider outlineProvider = new RevealOutlineProvider(x, y, r0, r1);
-        final float elevation = v.getElevation();
-
-        va.addListener(new AnimatorListenerAdapter() {
-            public void onAnimationStart(Animator animation) {
-                revealView.setOutlineProvider(outlineProvider);
-                revealView.setClipToOutline(true);
-                revealView.setTranslationZ(-elevation);
-            }
-
-            public void onAnimationEnd(Animator animation) {
-                revealView.setOutlineProvider(originalProvider);
-                revealView.setClipToOutline(false);
-                revealView.setTranslationZ(0);
-            }
-
-        });
-
-        va.addUpdateListener(new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator arg0) {
-                float progress = arg0.getAnimatedFraction();
-                outlineProvider.setProgress(progress);
-                revealView.invalidateOutline();
-                if (!Utilities.ATLEAST_LOLLIPOP_MR1) {
-                    revealView.invalidate();
-                }
-            }
-        });
-        return va;
-    }
-}
diff --git a/src/com/android/launcher3/util/VerticalFlingDetector.java b/src/com/android/launcher3/util/VerticalFlingDetector.java
new file mode 100644
index 0000000..7236c2d
--- /dev/null
+++ b/src/com/android/launcher3/util/VerticalFlingDetector.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+public class VerticalFlingDetector implements View.OnTouchListener {
+
+    private static final float CUSTOM_SLOP_MULTIPLIER = 2.2f;
+    private static final int SEC_IN_MILLIS = 1000;
+
+    private VelocityTracker mVelocityTracker;
+    private float mMinimumFlingVelocity;
+    private float mMaximumFlingVelocity;
+    private float mDownX, mDownY;
+    private boolean mShouldCheckFling;
+    private double mCustomTouchSlop;
+
+    public VerticalFlingDetector(Context context) {
+        ViewConfiguration vc = ViewConfiguration.get(context);
+        mMinimumFlingVelocity = vc.getScaledMinimumFlingVelocity();
+        mMaximumFlingVelocity = vc.getScaledMaximumFlingVelocity();
+        mCustomTouchSlop = CUSTOM_SLOP_MULTIPLIER * vc.getScaledTouchSlop();
+    }
+
+    @Override
+    public boolean onTouch(View v, MotionEvent ev) {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(ev);
+        switch (ev.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mDownX = ev.getX();
+                mDownY = ev.getY();
+                mShouldCheckFling = false;
+                break;
+            case MotionEvent.ACTION_MOVE:
+                if (mShouldCheckFling) {
+                    break;
+                }
+                if (Math.abs(ev.getY() - mDownY) > mCustomTouchSlop &&
+                        Math.abs(ev.getY() - mDownY) > Math.abs(ev.getX() - mDownX)) {
+                    mShouldCheckFling = true;
+                }
+                break;
+            case MotionEvent.ACTION_UP:
+                if (mShouldCheckFling) {
+                    mVelocityTracker.computeCurrentVelocity(SEC_IN_MILLIS, mMaximumFlingVelocity);
+                    // only when fling is detected in down direction
+                    if (mVelocityTracker.getYVelocity() > mMinimumFlingVelocity) {
+                        cleanUp();
+                        return true;
+                    }
+                }
+                // fall through.
+            case MotionEvent.ACTION_CANCEL:
+                cleanUp();
+        }
+        return false;
+    }
+
+    private void cleanUp() {
+        if (mVelocityTracker == null) {
+            return;
+        }
+        mVelocityTracker.recycle();
+        mVelocityTracker = null;
+    }
+}
diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
new file mode 100644
index 0000000..9bd2882
--- /dev/null
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnAttachStateChangeListener;
+import android.view.ViewTreeObserver.OnDrawListener;
+
+import com.android.launcher3.DeferredHandler;
+import com.android.launcher3.Launcher;
+
+import java.util.ArrayList;
+import java.util.concurrent.Executor;
+
+/**
+ * An executor which runs all the tasks after the first onDraw is called on the target view.
+ */
+public class ViewOnDrawExecutor implements Executor, OnDrawListener, Runnable,
+        OnAttachStateChangeListener {
+
+    private final ArrayList<Runnable> mTasks = new ArrayList<>();
+    private final DeferredHandler mHandler;
+
+    private Launcher mLauncher;
+    private View mAttachedView;
+    private boolean mCompleted;
+
+    private boolean mLoadAnimationCompleted;
+    private boolean mFirstDrawCompleted;
+
+    public ViewOnDrawExecutor(DeferredHandler handler) {
+        mHandler = handler;
+    }
+
+    public void attachTo(Launcher launcher) {
+        mLauncher = launcher;
+        mAttachedView = launcher.getWorkspace();
+        mAttachedView.addOnAttachStateChangeListener(this);
+
+        attachObserver();
+    }
+
+    private void attachObserver() {
+        if (!mCompleted) {
+            mAttachedView.getViewTreeObserver().addOnDrawListener(this);
+        }
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        mTasks.add(command);
+    }
+
+    @Override
+    public void onViewAttachedToWindow(View v) {
+        attachObserver();
+    }
+
+    @Override
+    public void onViewDetachedFromWindow(View v) { }
+
+    @Override
+    public void onDraw() {
+        mFirstDrawCompleted = true;
+        mAttachedView.post(this);
+    }
+
+    public void onLoadAnimationCompleted() {
+        mLoadAnimationCompleted = true;
+        if (mAttachedView != null) {
+            mAttachedView.post(this);
+        }
+    }
+
+    @Override
+    public void run() {
+        // Post the pending tasks after both onDraw and onLoadAnimationCompleted have been called.
+        if (mLoadAnimationCompleted && mFirstDrawCompleted && !mCompleted) {
+            for (final Runnable r : mTasks) {
+                mHandler.post(r);
+            }
+            markCompleted();
+        }
+    }
+
+    public void markCompleted() {
+        mTasks.clear();
+        mCompleted = true;
+        if (mAttachedView != null) {
+            mAttachedView.getViewTreeObserver().removeOnDrawListener(this);
+            mAttachedView.removeOnAttachStateChangeListener(this);
+        }
+        if (mLauncher != null) {
+            mLauncher.clearPendingExecutor(this);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
new file mode 100644
index 0000000..3926974
--- /dev/null
+++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
@@ -0,0 +1,241 @@
+package com.android.launcher3.util;
+
+import android.app.WallpaperManager;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.Choreographer;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
+
+/**
+ * Utility class to handle wallpaper scrolling along with workspace.
+ */
+public class WallpaperOffsetInterpolator implements Choreographer.FrameCallback {
+    private static final String TAG = "WPOffsetInterpolator";
+    private static final int ANIMATION_DURATION = 250;
+
+    // Don't use all the wallpaper for parallax until you have at least this many pages
+    private static final int MIN_PARALLAX_PAGE_SPAN = 4;
+
+    private final Choreographer mChoreographer;
+    private final Interpolator mInterpolator;
+    private final WallpaperManager mWallpaperManager;
+    private final Workspace mWorkspace;
+    private final boolean mIsRtl;
+
+    private IBinder mWindowToken;
+    private boolean mWallpaperIsLiveWallpaper;
+    private float mLastSetWallpaperOffsetSteps = 0;
+
+    private float mFinalOffset = 0.0f;
+    private float mCurrentOffset = 0.5f; // to force an initial update
+    private boolean mWaitingForUpdate;
+    private boolean mLockedToDefaultPage;
+
+    private boolean mAnimating;
+    private long mAnimationStartTime;
+    private float mAnimationStartOffset;
+    int mNumScreens;
+    int mNumPagesForWallpaperParallax;
+
+    public WallpaperOffsetInterpolator(Workspace workspace) {
+        mChoreographer = Choreographer.getInstance();
+        mInterpolator = new DecelerateInterpolator(1.5f);
+
+        mWorkspace = workspace;
+        mWallpaperManager = WallpaperManager.getInstance(workspace.getContext());
+        mIsRtl = Utilities.isRtl(workspace.getResources());
+    }
+
+    @Override
+    public void doFrame(long frameTimeNanos) {
+        updateOffset(false);
+    }
+
+    private void updateOffset(boolean force) {
+        if (mWaitingForUpdate || force) {
+            mWaitingForUpdate = false;
+            if (computeScrollOffset() && mWindowToken != null) {
+                try {
+                    mWallpaperManager.setWallpaperOffsets(mWindowToken, getCurrX(), 0.5f);
+                    setWallpaperOffsetSteps();
+                } catch (IllegalArgumentException e) {
+                    Log.e(TAG, "Error updating wallpaper offset: " + e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Locks the wallpaper offset to the offset in the default state of Launcher.
+     */
+    public void setLockToDefaultPage(boolean lockToDefaultPage) {
+        mLockedToDefaultPage = lockToDefaultPage;
+    }
+
+    public boolean isLockedToDefaultPage() {
+        return mLockedToDefaultPage;
+    }
+
+    public boolean computeScrollOffset() {
+        final float oldOffset = mCurrentOffset;
+        if (mAnimating) {
+            long durationSinceAnimation = System.currentTimeMillis() - mAnimationStartTime;
+            float t0 = durationSinceAnimation / (float) ANIMATION_DURATION;
+            float t1 = mInterpolator.getInterpolation(t0);
+            mCurrentOffset = mAnimationStartOffset +
+                    (mFinalOffset - mAnimationStartOffset) * t1;
+            mAnimating = durationSinceAnimation < ANIMATION_DURATION;
+        } else {
+            mCurrentOffset = mFinalOffset;
+        }
+
+        if (Math.abs(mCurrentOffset - mFinalOffset) > 0.0000001f) {
+            scheduleUpdate();
+        }
+        if (Math.abs(oldOffset - mCurrentOffset) > 0.0000001f) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * TODO: do different behavior if it's  a live wallpaper?
+     */
+    public float wallpaperOffsetForScroll(int scroll) {
+        // To match the default wallpaper behavior in the system, we default to either the left
+        // or right edge on initialization
+        int numScrollingPages = getNumScreensExcludingEmptyAndCustom();
+        if (mLockedToDefaultPage || numScrollingPages <= 1) {
+            return mIsRtl ? 1f : 0f;
+        }
+
+        // Distribute the wallpaper parallax over a minimum of MIN_PARALLAX_PAGE_SPAN workspace
+        // screens, not including the custom screen, and empty screens (if > MIN_PARALLAX_PAGE_SPAN)
+        if (mWallpaperIsLiveWallpaper) {
+            mNumPagesForWallpaperParallax = numScrollingPages;
+        } else {
+            mNumPagesForWallpaperParallax = Math.max(MIN_PARALLAX_PAGE_SPAN, numScrollingPages);
+        }
+
+        // Offset by the custom screen
+        int leftPageIndex;
+        int rightPageIndex;
+        if (mIsRtl) {
+            rightPageIndex = mWorkspace.numCustomPages();
+            leftPageIndex = rightPageIndex + numScrollingPages - 1;
+        } else {
+            leftPageIndex = mWorkspace.numCustomPages();
+            rightPageIndex = leftPageIndex + numScrollingPages - 1;
+        }
+
+        // Calculate the scroll range
+        int leftPageScrollX = mWorkspace.getScrollForPage(leftPageIndex);
+        int rightPageScrollX = mWorkspace.getScrollForPage(rightPageIndex);
+        int scrollRange = rightPageScrollX - leftPageScrollX;
+        if (scrollRange == 0) {
+            return 0f;
+        }
+
+        // Sometimes the left parameter of the pages is animated during a layout transition;
+        // this parameter offsets it to keep the wallpaper from animating as well
+        int adjustedScroll = scroll - leftPageScrollX -
+                mWorkspace.getLayoutTransitionOffsetForPage(0);
+        float offset = Utilities.boundToRange((float) adjustedScroll / scrollRange, 0f, 1f);
+
+        // The offset is now distributed 0..1 between the left and right pages that we care about,
+        // so we just map that between the pages that we are using for parallax
+        float rtlOffset = 0;
+        if (mIsRtl) {
+            // In RTL, the pages are right aligned, so adjust the offset from the end
+            rtlOffset = (float) ((mNumPagesForWallpaperParallax - 1) - (numScrollingPages - 1)) /
+                    (mNumPagesForWallpaperParallax - 1);
+        }
+        return rtlOffset + offset *
+                ((float) (numScrollingPages - 1) / (mNumPagesForWallpaperParallax - 1));
+    }
+
+    private float wallpaperOffsetForCurrentScroll() {
+        return wallpaperOffsetForScroll(mWorkspace.getScrollX());
+    }
+
+    private int numEmptyScreensToIgnore() {
+        int numScrollingPages = mWorkspace.getChildCount() - mWorkspace.numCustomPages();
+        if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && mWorkspace.hasExtraEmptyScreen()) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    private int getNumScreensExcludingEmptyAndCustom() {
+        return mWorkspace.getChildCount() - numEmptyScreensToIgnore() - mWorkspace.numCustomPages();
+    }
+
+    public void syncWithScroll() {
+        float offset = wallpaperOffsetForCurrentScroll();
+        setFinalX(offset);
+        updateOffset(true);
+    }
+
+    public float getCurrX() {
+        return mCurrentOffset;
+    }
+
+    public float getFinalX() {
+        return mFinalOffset;
+    }
+
+    private void animateToFinal() {
+        mAnimating = true;
+        mAnimationStartOffset = mCurrentOffset;
+        mAnimationStartTime = System.currentTimeMillis();
+    }
+
+    private void setWallpaperOffsetSteps() {
+        // Set wallpaper offset steps (1 / (number of screens - 1))
+        float xOffset = 1.0f / (mNumPagesForWallpaperParallax - 1);
+        if (xOffset != mLastSetWallpaperOffsetSteps) {
+            mWallpaperManager.setWallpaperOffsetSteps(xOffset, 1.0f);
+            mLastSetWallpaperOffsetSteps = xOffset;
+        }
+    }
+
+    public void setFinalX(float x) {
+        scheduleUpdate();
+        mFinalOffset = Math.max(0f, Math.min(x, 1f));
+        if (getNumScreensExcludingEmptyAndCustom() != mNumScreens) {
+            if (mNumScreens > 0 && Float.compare(mCurrentOffset, mFinalOffset) != 0) {
+                // Don't animate if we're going from 0 screens, or if the final offset is the same
+                // as the current offset
+                animateToFinal();
+            }
+            mNumScreens = getNumScreensExcludingEmptyAndCustom();
+        }
+    }
+
+    private void scheduleUpdate() {
+        if (!mWaitingForUpdate) {
+            mChoreographer.postFrameCallback(this);
+            mWaitingForUpdate = true;
+        }
+    }
+
+    public void jumpToFinal() {
+        mCurrentOffset = mFinalOffset;
+    }
+
+    public void onResume() {
+        mWallpaperIsLiveWallpaper = mWallpaperManager.getWallpaperInfo() != null;
+        // Force the wallpaper offset steps to be set again, because another app might have changed
+        // them
+        mLastSetWallpaperOffsetSteps = 0f;
+    }
+
+    public void setWindowToken(IBinder token) {
+        mWindowToken = token;
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/util/WallpaperUtils.java b/src/com/android/launcher3/util/WallpaperUtils.java
deleted file mode 100644
index b9fccbc..0000000
--- a/src/com/android/launcher3/util/WallpaperUtils.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.util;
-
-import android.annotation.TargetApi;
-import android.app.WallpaperManager;
-import android.content.SharedPreferences;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.os.Build;
-import android.view.WindowManager;
-
-import com.android.launcher3.Utilities;
-
-/**
- * Utility methods for wallpaper management.
- */
-public final class WallpaperUtils {
-
-    public static final String WALLPAPER_WIDTH_KEY = "wallpaper.width";
-    public static final String WALLPAPER_HEIGHT_KEY = "wallpaper.height";
-    public static final float WALLPAPER_SCREENS_SPAN = 2f;
-
-    public static void suggestWallpaperDimension(Resources res,
-            final SharedPreferences sharedPrefs,
-            WindowManager windowManager,
-            final WallpaperManager wallpaperManager, boolean fallBackToDefaults) {
-        final Point defaultWallpaperSize = WallpaperUtils.getDefaultWallpaperSize(res, windowManager);
-        // If we have saved a wallpaper width/height, use that instead
-
-        int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, -1);
-        int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, -1);
-
-        if (savedWidth == -1 || savedHeight == -1) {
-            if (!fallBackToDefaults) {
-                return;
-            } else {
-                savedWidth = defaultWallpaperSize.x;
-                savedHeight = defaultWallpaperSize.y;
-            }
-        }
-
-        if (savedWidth != wallpaperManager.getDesiredMinimumWidth() ||
-                savedHeight != wallpaperManager.getDesiredMinimumHeight()) {
-            wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight);
-        }
-    }
-
-    /**
-     * As a ratio of screen height, the total distance we want the parallax effect to span
-     * horizontally
-     */
-    public static float wallpaperTravelToScreenWidthRatio(int width, int height) {
-        float aspectRatio = width / (float) height;
-
-        // At an aspect ratio of 16/10, the wallpaper parallax effect should span 1.5 * screen width
-        // At an aspect ratio of 10/16, the wallpaper parallax effect should span 1.2 * screen width
-        // We will use these two data points to extrapolate how much the wallpaper parallax effect
-        // to span (ie travel) at any aspect ratio:
-
-        final float ASPECT_RATIO_LANDSCAPE = 16/10f;
-        final float ASPECT_RATIO_PORTRAIT = 10/16f;
-        final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE = 1.5f;
-        final float WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT = 1.2f;
-
-        // To find out the desired width at different aspect ratios, we use the following two
-        // formulas, where the coefficient on x is the aspect ratio (width/height):
-        //   (16/10)x + y = 1.5
-        //   (10/16)x + y = 1.2
-        // We solve for x and y and end up with a final formula:
-        final float x =
-            (WALLPAPER_WIDTH_TO_SCREEN_RATIO_LANDSCAPE - WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT) /
-            (ASPECT_RATIO_LANDSCAPE - ASPECT_RATIO_PORTRAIT);
-        final float y = WALLPAPER_WIDTH_TO_SCREEN_RATIO_PORTRAIT - x * ASPECT_RATIO_PORTRAIT;
-        return x * aspectRatio + y;
-    }
-
-    private static Point sDefaultWallpaperSize;
-
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
-    public static Point getDefaultWallpaperSize(Resources res, WindowManager windowManager) {
-        if (sDefaultWallpaperSize == null) {
-            Point minDims = new Point();
-            Point maxDims = new Point();
-            windowManager.getDefaultDisplay().getCurrentSizeRange(minDims, maxDims);
-
-            int maxDim = Math.max(maxDims.x, maxDims.y);
-            int minDim = Math.max(minDims.x, minDims.y);
-
-            if (Utilities.ATLEAST_JB_MR1) {
-                Point realSize = new Point();
-                windowManager.getDefaultDisplay().getRealSize(realSize);
-                maxDim = Math.max(realSize.x, realSize.y);
-                minDim = Math.min(realSize.x, realSize.y);
-            }
-
-            // We need to ensure that there is enough extra space in the wallpaper
-            // for the intended parallax effects
-            final int defaultWidth, defaultHeight;
-            if (res.getConfiguration().smallestScreenWidthDp >= 720) {
-                defaultWidth = (int) (maxDim * wallpaperTravelToScreenWidthRatio(maxDim, minDim));
-                defaultHeight = maxDim;
-            } else {
-                defaultWidth = Math.max((int) (minDim * WALLPAPER_SCREENS_SPAN), maxDim);
-                defaultHeight = maxDim;
-            }
-            sDefaultWallpaperSize = new Point(defaultWidth, defaultHeight);
-        }
-        return sDefaultWallpaperSize;
-    }
-}
diff --git a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
index a569850..486b18e 100644
--- a/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddShortcutInfo.java
@@ -35,10 +35,4 @@
         componentName = new ComponentName(activityInfo.packageName, activityInfo.name);
         itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
     }
-
-    @Override
-    public String toString() {
-        return String.format("PendingAddShortcutInfo package=%s, name=%s",
-                activityInfo.packageName, activityInfo.name);
-    }
 }
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index fcb714f..f800ac4 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.widget;
 
 import android.appwidget.AppWidgetHostView;
+import android.content.Context;
 import android.os.Bundle;
 import android.os.Parcelable;
 
@@ -37,14 +38,14 @@
     public AppWidgetHostView boundWidget;
     public Bundle bindOptions = null;
 
-    public PendingAddWidgetInfo(Launcher launcher, LauncherAppWidgetProviderInfo i, Parcelable data) {
+    public PendingAddWidgetInfo(Context context, LauncherAppWidgetProviderInfo i) {
         if (i.isCustomWidget) {
             itemType = LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
         } else {
             itemType = LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
         }
         this.info = i;
-        user = AppWidgetManagerCompat.getInstance(launcher).getUser(i);
+        user = AppWidgetManagerCompat.getInstance(context).getUser(i);
         componentName = i.provider;
         previewImage = i.previewImage;
         icon = i.icon;
@@ -58,10 +59,4 @@
     public boolean isCustomWidget() {
         return itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
     }
-
-    @Override
-    public String toString() {
-        return String.format("PendingAddWidgetInfo package=%s, name=%s",
-                componentName.getPackageName(), componentName.getShortClassName());
-    }
 }
diff --git a/src/com/android/launcher3/widget/PendingItemPreviewProvider.java b/src/com/android/launcher3/widget/PendingItemPreviewProvider.java
new file mode 100644
index 0000000..eaa0bb3
--- /dev/null
+++ b/src/com/android/launcher3/widget/PendingItemPreviewProvider.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.widget;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.view.View;
+
+import com.android.launcher3.HolographicOutlineHelper;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.graphics.DragPreviewProvider;
+
+/**
+ * Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts
+ * dragged from the widget tray.
+ */
+public class PendingItemPreviewProvider extends DragPreviewProvider {
+
+    private final PendingAddItemInfo mAddInfo;
+    private final Bitmap mPreviewBitmap;
+
+    public PendingItemPreviewProvider(View view, PendingAddItemInfo addInfo, Bitmap preview) {
+        super(view);
+        mAddInfo = addInfo;
+        mPreviewBitmap = preview;
+    }
+
+    @Override
+    public Bitmap createDragOutline(Canvas canvas) {
+        Workspace workspace = Launcher.getLauncher(mView.getContext()).getWorkspace();
+        int[] size = workspace.estimateItemSize(mAddInfo, false);
+
+        int w = size[0];
+        int h = size[1];
+        final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ALPHA_8);
+        canvas.setBitmap(b);
+
+        Rect src = new Rect(0, 0, mPreviewBitmap.getWidth(), mPreviewBitmap.getHeight());
+        float scaleFactor = Math.min((w - DRAG_BITMAP_PADDING) / (float) mPreviewBitmap.getWidth(),
+                (h - DRAG_BITMAP_PADDING) / (float) mPreviewBitmap.getHeight());
+        int scaledWidth = (int) (scaleFactor * mPreviewBitmap.getWidth());
+        int scaledHeight = (int) (scaleFactor * mPreviewBitmap.getHeight());
+        Rect dst = new Rect(0, 0, scaledWidth, scaledHeight);
+
+        // center the image
+        dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
+
+        canvas.drawBitmap(mPreviewBitmap, src, dst, null);
+
+        // Don't clip alpha values for the drag outline if we're using the default widget preview
+        boolean clipAlpha = !(mAddInfo instanceof PendingAddWidgetInfo &&
+                (((PendingAddWidgetInfo) mAddInfo).previewImage == 0));
+        HolographicOutlineHelper.obtain(mView.getContext())
+                .applyExpensiveOutlineWithBlur(b, canvas, clipAlpha);
+        canvas.setBitmap(null);
+
+        return b;
+    }
+}
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 94bbd92..293585d 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -17,8 +17,6 @@
 package com.android.launcher3.widget;
 
 import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.util.AttributeSet;
@@ -31,16 +29,15 @@
 import android.widget.TextView;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.SimpleOnStylusPressListener;
 import com.android.launcher3.R;
 import com.android.launcher3.StylusEventHelper;
 import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.WidgetPreviewLoader.PreviewLoadRequest;
-import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.model.WidgetItem;
 
 /**
  * Represents the individual cell of the widget inside the widget tray. The preview is drawn
@@ -71,14 +68,13 @@
     private TextView mWidgetName;
     private TextView mWidgetDims;
 
-    private String mDimensionsFormatString;
-    private Object mInfo;
+    private WidgetItem mItem;
 
     private WidgetPreviewLoader mWidgetPreviewLoader;
     private PreviewLoadRequest mActiveRequest;
     private StylusEventHelper mStylusEventHelper;
 
-    private Launcher mLauncher;
+    private final Launcher mLauncher;
 
     public WidgetCell(Context context) {
         this(context, null);
@@ -92,14 +88,13 @@
         super(context, attrs, defStyle);
 
         final Resources r = context.getResources();
-        mLauncher = (Launcher) context;
-        mStylusEventHelper = new StylusEventHelper(this);
+        mLauncher = Launcher.getLauncher(context);
+        mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
 
-        mDimensionsFormatString = r.getString(R.string.widget_dims_format);
         setContainerWidth();
         setWillNotDraw(false);
         setClipToPadding(false);
-        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
+        setAccessibilityDelegate(mLauncher.getAccessibilityDelegate());
     }
 
     private void setContainerWidth() {
@@ -135,33 +130,20 @@
         }
     }
 
-    /**
-     * Apply the widget provider info to the view.
-     */
-    public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info,
-            WidgetPreviewLoader loader) {
-
-        InvariantDeviceProfile profile =
-                LauncherAppState.getInstance().getInvariantDeviceProfile();
-        mInfo = info;
-        // TODO(hyunyoungs): setup a cache for these labels.
-        mWidgetName.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
-        int hSpan = Math.min(info.spanX, profile.numColumns);
-        int vSpan = Math.min(info.spanY, profile.numRows);
-        mWidgetDims.setText(String.format(mDimensionsFormatString, hSpan, vSpan));
+    public void applyFromCellItem(WidgetItem item, WidgetPreviewLoader loader) {
+        mItem = item;
+        mWidgetName.setText(mItem.label);
+        mWidgetDims.setText(getContext().getString(R.string.widget_dims_format,
+                mItem.spanX, mItem.spanY));
+        mWidgetDims.setContentDescription(getContext().getString(
+                R.string.widget_accessible_dims_format, mItem.spanX, mItem.spanY));
         mWidgetPreviewLoader = loader;
-    }
 
-    /**
-     * Apply the resolve info to the view.
-     */
-    public void applyFromResolveInfo(
-            PackageManager pm, ResolveInfo info, WidgetPreviewLoader loader) {
-        mInfo = info;
-        CharSequence label = info.loadLabel(pm);
-        mWidgetName.setText(label);
-        mWidgetDims.setText(String.format(mDimensionsFormatString, 1, 1));
-        mWidgetPreviewLoader = loader;
+        if (item.activityInfo != null) {
+            setTag(new PendingAddShortcutInfo(item.activityInfo));
+        } else {
+            setTag(new PendingAddWidgetInfo(mLauncher, item.widgetInfo));
+        }
     }
 
     public int[] getPreviewSize() {
@@ -190,7 +172,7 @@
             Log.d(TAG, String.format("[tag=%s] ensurePreview (%d, %d):",
                     getTagToString(), size[0], size[1]));
         }
-        mActiveRequest = mWidgetPreviewLoader.getPreview(mInfo, size[0], size[1], this);
+        mActiveRequest = mWidgetPreviewLoader.getPreview(mItem, size[0], size[1], this);
     }
 
     @Override
@@ -211,7 +193,7 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         boolean handled = super.onTouchEvent(ev);
-        if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) {
+        if (mStylusEventHelper.onMotionEvent(ev)) {
             return true;
         }
         return handled;
@@ -227,4 +209,9 @@
         }
         return "";
     }
+
+    @Override
+    public CharSequence getAccessibilityClassName() {
+        return WidgetCell.class.getName();
+    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 5d3af52..049871f 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -10,13 +10,16 @@
 import android.view.View;
 
 import com.android.launcher3.AppWidgetResizeFrame;
-import com.android.launcher3.DragController;
-import com.android.launcher3.DragLayer;
 import com.android.launcher3.DragSource;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.util.Thunk;
 
 public class WidgetHostViewLoader implements DragController.DragListener {
@@ -46,7 +49,7 @@
     }
 
     @Override
-    public void onDragStart(DragSource source, Object info, int dragAction) { }
+    public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) { }
 
     @Override
     public void onDragEnd() {
@@ -150,15 +153,15 @@
         return true;
     }
 
-    public static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) {
+    public static Bundle getDefaultOptionsForWidget(Context context, PendingAddWidgetInfo info) {
         Bundle options = null;
-        Rect rect = new Rect();
         if (Utilities.ATLEAST_JB_MR1) {
-            AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, rect);
-            Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher,
+            Rect rect = new Rect();
+            AppWidgetResizeFrame.getWidgetSizeRanges(context, info.spanX, info.spanY, rect);
+            Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context,
                     info.componentName, null);
 
-            float density = launcher.getResources().getDisplayMetrics().density;
+            float density = context.getResources().getDisplayMetrics().density;
             int xPaddingDips = (int) ((padding.left + padding.right) / density);
             int yPaddingDips = (int) ((padding.top + padding.bottom) / density);
 
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index c8e8cf8..89c44c8 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -25,16 +25,16 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.Toast;
 
 import com.android.launcher3.BaseContainerView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeleteDropTarget;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.DragController;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.Folder;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.folder.Folder;
 import com.android.launcher3.IconCache;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
@@ -44,8 +44,12 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.Workspace;
+import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.Thunk;
+import com.android.launcher3.util.TransformingTouchDelegate;
 
 /**
  * The widgets list view container.
@@ -60,9 +64,12 @@
     private DragController mDragController;
     private IconCache mIconCache;
 
+    private final Rect mTmpBgPaddingRect = new Rect();
+
     /* Recycler view related member variables */
     private WidgetsRecyclerView mRecyclerView;
     private WidgetsListAdapter mAdapter;
+    private TransformingTouchDelegate mRecyclerViewTouchDelegate;
 
     /* Touch handling related member variables. */
     private Toast mWidgetInstructionToast;
@@ -80,9 +87,9 @@
 
     public WidgetsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mLauncher = (Launcher) context;
+        mLauncher = Launcher.getLauncher(context);
         mDragController = mLauncher.getDragController();
-        mAdapter = new WidgetsListAdapter(context, this, this, mLauncher);
+        mAdapter = new WidgetsListAdapter(this, this, context);
         mIconCache = (LauncherAppState.getInstance()).getIconCache();
         if (LOGD) {
             Log.d(TAG, "WidgetsContainerView constructor");
@@ -90,11 +97,29 @@
     }
 
     @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        getRevealView().getBackground().getPadding(mTmpBgPaddingRect);
+        mRecyclerViewTouchDelegate.setBounds(
+                mRecyclerView.getLeft() - mTmpBgPaddingRect.left,
+                mRecyclerView.getTop() - mTmpBgPaddingRect.top,
+                mRecyclerView.getRight() + mTmpBgPaddingRect.right,
+                mRecyclerView.getBottom() + mTmpBgPaddingRect.bottom);
+    }
+
+    @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mRecyclerView = (WidgetsRecyclerView) getContentView().findViewById(R.id.widgets_list_view);
         mRecyclerView.setAdapter(mAdapter);
         mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+        mRecyclerViewTouchDelegate = new TransformingTouchDelegate(mRecyclerView);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        ((View) mRecyclerView.getParent()).setTouchDelegate(mRecyclerViewTouchDelegate);
     }
 
     //
@@ -184,7 +209,7 @@
 
         // Compose the drag image
         Bitmap preview;
-        float scale = 1f;
+        final float scale;
         final Rect bounds = image.getBitmapBounds();
 
         if (createItemInfo instanceof PendingAddWidgetInfo) {
@@ -221,17 +246,14 @@
             scale = ((float) mLauncher.getDeviceProfile().iconSizePx) / preview.getWidth();
         }
 
-        // Don't clip alpha values for the drag outline if we're using the default widget preview
-        boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo &&
-                (((PendingAddWidgetInfo) createItemInfo).previewImage == 0));
+        // Since we are not going through the workspace for starting the drag, set drag related
+        // information on the workspace before starting the drag.
+        mLauncher.getWorkspace().prepareDragWithProvider(
+                new PendingItemPreviewProvider(v, createItemInfo, preview));
 
         // Start the drag
-        mLauncher.lockScreenOrientation();
-        mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, preview, clipAlpha);
         mDragController.startDrag(image, preview, this, createItemInfo,
-                bounds, DragController.DRAG_ACTION_COPY, scale);
-
-        preview.recycle();
+                bounds, scale, new DragOptions());
         return true;
     }
 
@@ -241,7 +263,7 @@
 
     @Override
     public boolean supportsFlingToDelete() {
-        return false;
+        return true;
     }
 
     @Override
@@ -294,7 +316,7 @@
                 int currentScreen = mLauncher.getCurrentWorkspaceScreen();
                 Workspace workspace = (Workspace) target;
                 CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
-                ItemInfo itemInfo = (ItemInfo) d.dragInfo;
+                ItemInfo itemInfo = d.dragInfo;
                 if (layout != null) {
                     showOutOfSpaceMessage =
                             !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
@@ -307,22 +329,6 @@
         }
     }
 
-    //
-    // Container rendering related.
-    //
-    @Override
-    protected void onUpdateBgPadding(Rect padding, Rect bgPadding) {
-        if (Utilities.isRtl(getResources())) {
-            getContentView().setPadding(0, bgPadding.top,
-                    bgPadding.right, bgPadding.bottom);
-            mRecyclerView.updateBackgroundPadding(new Rect(bgPadding.left, 0, 0, 0));
-        } else {
-            getContentView().setPadding(bgPadding.left, bgPadding.top,
-                    0, bgPadding.bottom);
-            mRecyclerView.updateBackgroundPadding(new Rect(0, 0, bgPadding.right, 0));
-        }
-    }
-
     /**
      * Initialize the widget data model.
      */
@@ -330,6 +336,11 @@
         mRecyclerView.setWidgets(model);
         mAdapter.setWidgetsModel(model);
         mAdapter.notifyDataSetChanged();
+
+        View loader = getContentView().findViewById(R.id.loader);
+        if (loader != null) {
+            ((ViewGroup) getContentView()).removeView(loader);
+        }
     }
 
     public boolean isEmpty() {
@@ -342,4 +353,9 @@
         }
         return mWidgetPreviewLoader;
     }
+
+    @Override
+    public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
+        targetParent.containerType = LauncherLogProto.WIDGETS;
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index 885d96f..6b8ea49 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -17,8 +17,6 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
 import android.os.Build;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.Adapter;
@@ -30,14 +28,12 @@
 import android.widget.LinearLayout;
 
 import com.android.launcher3.BubbleTextView;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.model.WidgetItem;
 import com.android.launcher3.model.WidgetsModel;
 
 import java.util.List;
@@ -55,29 +51,25 @@
     private static final String TAG = "WidgetsListAdapter";
     private static final boolean DEBUG = false;
 
-    private Launcher mLauncher;
-    private LayoutInflater mLayoutInflater;
+    private final WidgetPreviewLoader mWidgetPreviewLoader;
+    private final LayoutInflater mLayoutInflater;
+
+    private final View.OnClickListener mIconClickListener;
+    private final View.OnLongClickListener mIconLongClickListener;
 
     private WidgetsModel mWidgetsModel;
-    private WidgetPreviewLoader mWidgetPreviewLoader;
 
-    private View.OnClickListener mIconClickListener;
-    private View.OnLongClickListener mIconLongClickListener;
+    private final int mIndent;
 
-    private static final int PRESET_INDENT_SIZE_TABLET = 56;
-    private int mIndent = 0;
-
-    public WidgetsListAdapter(Context context,
-            View.OnClickListener iconClickListener,
+    public WidgetsListAdapter(View.OnClickListener iconClickListener,
             View.OnLongClickListener iconLongClickListener,
-            Launcher launcher) {
+            Context context) {
         mLayoutInflater = LayoutInflater.from(context);
+        mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
 
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
-        mLauncher = launcher;
-
-        setContainerHeight();
+        mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
     }
 
     public void setWidgetsModel(WidgetsModel w) {
@@ -94,9 +86,9 @@
 
     @Override
     public void onBindViewHolder(WidgetsRowViewHolder holder, int pos) {
-        List<Object> infoList = mWidgetsModel.getSortedWidgets(pos);
+        List<WidgetItem> infoList = mWidgetsModel.getSortedWidgets(pos);
 
-        ViewGroup row = ((ViewGroup) holder.getContent().findViewById(R.id.widgets_cell_list));
+        ViewGroup row = holder.cellContainer;
         if (DEBUG) {
             Log.d(TAG, String.format(
                     "onBindViewHolder [pos=%d, widget#=%d, row.getChildCount=%d]",
@@ -129,27 +121,12 @@
         }
 
         // Bind the views in the application info section.
-        PackageItemInfo infoOut = mWidgetsModel.getPackageItemInfo(pos);
-        BubbleTextView tv = ((BubbleTextView) holder.getContent().findViewById(R.id.section));
-        tv.applyFromPackageItemInfo(infoOut);
+        holder.title.applyFromPackageItemInfo(mWidgetsModel.getPackageItemInfo(pos));
 
         // Bind the view in the widget horizontal tray region.
-        if (getWidgetPreviewLoader() == null) {
-            return;
-        }
         for (int i=0; i < infoList.size(); i++) {
             WidgetCell widget = (WidgetCell) row.getChildAt(i);
-            if (infoList.get(i) instanceof LauncherAppWidgetProviderInfo) {
-                LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) infoList.get(i);
-                PendingAddWidgetInfo pawi = new PendingAddWidgetInfo(mLauncher, info, null);
-                widget.setTag(pawi);
-                widget.applyFromAppWidgetProviderInfo(info, mWidgetPreviewLoader);
-            } else if (infoList.get(i) instanceof ResolveInfo) {
-                ResolveInfo info = (ResolveInfo) infoList.get(i);
-                PendingAddShortcutInfo pasi = new PendingAddShortcutInfo(info.activityInfo);
-                widget.setTag(pasi);
-                widget.applyFromResolveInfo(mLauncher.getPackageManager(), info, mWidgetPreviewLoader);
-            }
+            widget.applyFromCellItem(infoList.get(i), mWidgetPreviewLoader);
             widget.ensurePreview();
             widget.setVisibility(View.VISIBLE);
         }
@@ -179,10 +156,9 @@
 
     @Override
     public void onViewRecycled(WidgetsRowViewHolder holder) {
-        ViewGroup row = ((ViewGroup) holder.getContent().findViewById(R.id.widgets_cell_list));
-
-        for (int i = 0; i < row.getChildCount(); i++) {
-            WidgetCell widget = (WidgetCell) row.getChildAt(i);
+        int total = holder.cellContainer.getChildCount();
+        for (int i = 0; i < total; i++) {
+            WidgetCell widget = (WidgetCell) holder.cellContainer.getChildAt(i);
             widget.clear();
         }
     }
@@ -199,19 +175,4 @@
     public long getItemId(int pos) {
         return pos;
     }
-
-    private WidgetPreviewLoader getWidgetPreviewLoader() {
-        if (mWidgetPreviewLoader == null) {
-            mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
-        }
-        return mWidgetPreviewLoader;
-    }
-
-    private void setContainerHeight() {
-        Resources r = mLauncher.getResources();
-        DeviceProfile profile = mLauncher.getDeviceProfile();
-        if (profile.isLargeTablet || profile.isTablet) {
-            mIndent = Utilities.pxFromDp(PRESET_INDENT_SIZE_TABLET, r.getDisplayMetrics());
-        }
-    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
index fe9c51c..2560661 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -23,7 +23,6 @@
 import android.util.AttributeSet;
 import android.view.View;
 import com.android.launcher3.BaseRecyclerView;
-import com.android.launcher3.R;
 import com.android.launcher3.model.PackageItemInfo;
 import com.android.launcher3.model.WidgetsModel;
 
@@ -34,7 +33,6 @@
 
     private static final String TAG = "WidgetsRecyclerView";
     private WidgetsModel mWidgets;
-    private ScrollPositionState mScrollPosState = new ScrollPositionState();
 
     public WidgetsRecyclerView(Context context) {
         this(context, null);
@@ -58,6 +56,9 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         addOnItemTouchListener(this);
+        // create a layout manager with Launcher's context so that scroll position
+        // can be preserved during screen rotation.
+        setLayoutManager(new LinearLayoutManager(getContext()));
     }
 
     public int getFastScrollerTrackColor(int defaultTrackColor) {
@@ -89,22 +90,16 @@
     @Override
     public String scrollToPositionAtProgress(float touchFraction) {
         // Skip early if widgets are not bound.
-        if (mWidgets == null) {
-            return "";
-        }
-
-        // Skip early if there are no widgets.
-        int rowCount = mWidgets.getPackageSize();
-        if (rowCount == 0) {
+        if (isModelNotReady()) {
             return "";
         }
 
         // Stop the scroller if it is scrolling
         stopScroll();
 
-        getCurScrollState(mScrollPosState, -1);
+        int rowCount = mWidgets.getPackageSize();
         float pos = rowCount * touchFraction;
-        int availableScrollHeight = getAvailableScrollHeight(rowCount);
+        int availableScrollHeight = getAvailableScrollHeight();
         LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager());
         layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction));
 
@@ -119,61 +114,49 @@
     @Override
     public void onUpdateScrollbar(int dy) {
         // Skip early if widgets are not bound.
-        if (mWidgets == null) {
-            return;
-        }
-
-        // Skip early if there are no widgets.
-        int rowCount = mWidgets.getPackageSize();
-        if (rowCount == 0) {
-            mScrollbar.setThumbOffset(-1, -1);
+        if (isModelNotReady()) {
             return;
         }
 
         // Skip early if, there no child laid out in the container.
-        getCurScrollState(mScrollPosState, -1);
-        if (mScrollPosState.rowIndex < 0) {
+        int scrollY = getCurrentScrollY();
+        if (scrollY < 0) {
             mScrollbar.setThumbOffset(-1, -1);
             return;
         }
 
-        synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount);
-    }
-
-    /**
-     * Returns the current scroll state.
-     */
-    protected void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask) {
-        stateOut.rowIndex = -1;
-        stateOut.rowTopOffset = -1;
-        stateOut.itemPos = -1;
-
-        // Skip early if widgets are not bound.
-        if (mWidgets == null) {
-            return;
-        }
-
-        // Return early if there are no items
-        int rowCount = mWidgets.getPackageSize();
-        if (rowCount == 0) {
-            return;
-        }
-        View child = getChildAt(0);
-        int position = getChildPosition(child);
-
-        stateOut.rowIndex = position;
-        stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child);
-        stateOut.itemPos = position;
+        synchronizeScrollBarThumbOffsetToViewScroll(scrollY, getAvailableScrollHeight());
     }
 
     @Override
-    protected int getTop(int rowIndex) {
-        if (getChildCount() == 0) {
-            return 0;
+    public int getCurrentScrollY() {
+        // Skip early if widgets are not bound.
+        if (isModelNotReady() || getChildCount() == 0) {
+            return -1;
         }
 
-        // All the rows are the same height, return any child height
         View child = getChildAt(0);
-        return child.getMeasuredHeight() * rowIndex;
+        int rowIndex = getChildPosition(child);
+        int y = (child.getMeasuredHeight() * rowIndex);
+        int offset = getLayoutManager().getDecoratedTop(child);
+
+        return getPaddingTop() + y - offset;
+    }
+
+    /**
+     * Returns the available scroll height:
+     *   AvailableScrollHeight = Total height of the all items - last page height
+     */
+    @Override
+    protected int getAvailableScrollHeight() {
+        View child = getChildAt(0);
+        int height = child.getMeasuredHeight() * mWidgets.getPackageSize();
+        int totalHeight = getPaddingTop() + height + getPaddingBottom();
+        int availableScrollHeight = totalHeight - getVisibleHeight();
+        return availableScrollHeight;
+    }
+
+    private boolean isModelNotReady() {
+        return mWidgets == null || mWidgets.getPackageSize() == 0;
     }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
index 249559a..bc85db6 100644
--- a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
+++ b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
@@ -16,21 +16,20 @@
 package com.android.launcher3.widget;
 
 import android.support.v7.widget.RecyclerView.ViewHolder;
-import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.R;
 
 public class WidgetsRowViewHolder extends ViewHolder {
 
-    ViewGroup mContent;
+    public final ViewGroup cellContainer;
+    public final BubbleTextView title;
 
     public WidgetsRowViewHolder(ViewGroup v) {
         super(v);
-        mContent = v;
-    }
 
-    ViewGroup getContent() {
-        return mContent;
+        cellContainer = (ViewGroup) v.findViewById(R.id.widgets_cell_list);
+        title = (BubbleTextView) v.findViewById(R.id.section);
     }
 }
diff --git a/src_config/com/android/launcher3/config/FeatureFlags.java b/src_config/com/android/launcher3/config/FeatureFlags.java
new file mode 100644
index 0000000..8ee5497
--- /dev/null
+++ b/src_config/com/android/launcher3/config/FeatureFlags.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.config;
+
+/**
+ * Defines a set of flags used to control various launcher behaviors
+ */
+public final class FeatureFlags {
+    private FeatureFlags() {}
+
+    // Custom flags go below this
+    public static boolean LAUNCHER3_DISABLE_ICON_NORMALIZATION = false;
+    // As opposed to the new spring-loaded workspace.
+    public static boolean LAUNCHER3_LEGACY_WORKSPACE_DND = false;
+    public static boolean LAUNCHER3_LEGACY_FOLDER_ICON = false;
+    public static boolean LAUNCHER3_USE_SYSTEM_DRAG_DRIVER = true;
+    public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
+    public static boolean LAUNCHER3_ALL_APPS_PULL_UP = true;
+
+    // Feature flag to enable moving the QSB on the 0th screen of the workspace.
+    public static final boolean QSB_ON_FIRST_SCREEN = true;
+    // When enabled the all-apps icon is not added to the hotseat.
+    public static final boolean NO_ALL_APPS_ICON = true;
+    // When enabled fling down gesture on the first workspace triggers search.
+    public static final boolean PULLDOWN_SEARCH = false;
+    // When enabled the status bar may show dark icons based on the top of the wallpaper.
+    public static final boolean LIGHT_STATUS_BAR = false;
+}
diff --git a/src/com/android/launcher3/config/ProviderConfig.java b/src_config/com/android/launcher3/config/ProviderConfig.java
similarity index 93%
rename from src/com/android/launcher3/config/ProviderConfig.java
rename to src_config/com/android/launcher3/config/ProviderConfig.java
index e8930d0..1d964b1 100644
--- a/src/com/android/launcher3/config/ProviderConfig.java
+++ b/src_config/com/android/launcher3/config/ProviderConfig.java
@@ -19,4 +19,6 @@
 public class ProviderConfig {
 
     public static final String AUTHORITY = "com.android.launcher3.settings".intern();
+
+    public static boolean IS_DOGFOOD_BUILD = true;
 }
diff --git a/tests/Android.mk b/tests/Android.mk
index d82f0b3..61ee220 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -16,15 +16,12 @@
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
-src_dirs := src
 LOCAL_MODULE_TAGS := tests
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-LOCAL_AAPT_FLAGS := --auto-add-overlay
 
-LOCAL_SDK_VERSION := 23
+LOCAL_SDK_VERSION := current
 
 LOCAL_PACKAGE_NAME := Launcher3Tests
 
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 8acc5e9..afe8952 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -20,12 +20,14 @@
 
     <uses-sdk tools:overrideLibrary="android.support.test.uiautomator.v18"/>
 
-    <application>
+    <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
     </application>
 
     <instrumentation
-        android:name="android.test.InstrumentationTestRunner"
+        android:functionalTest="false"
+        android:handleProfiling="false"
+        android:name="android.support.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.launcher3" >
     </instrumentation>
 </manifest>
diff --git a/tests/res/values/string.xml b/tests/res/values/string.xml
deleted file mode 100644
index 3c1ec5c..0000000
--- a/tests/res/values/string.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<!-- Copyright (C) 2015 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.
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <!-- Dummy string for tests. [DO NOT TRANSLATE] -->
-    <string name="dummy" >Dummy string for tests.</string>
-
-</resources>
diff --git a/tests/src/com/android/launcher3/BindWidgetTest.java b/tests/src/com/android/launcher3/BindWidgetTest.java
new file mode 100644
index 0000000..5c5069f
--- /dev/null
+++ b/tests/src/com/android/launcher3/BindWidgetTest.java
@@ -0,0 +1,343 @@
+package com.android.launcher3;
+
+import android.annotation.TargetApi;
+import android.appwidget.AppWidgetHost;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionParams;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.test.uiautomator.UiSelector;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.ui.LauncherInstrumentationTestCase;
+import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.WidgetHostViewLoader;
+
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for bind widget flow.
+ *
+ * Note running these tests will clear the workspace on the device.
+ */
+@LargeTest
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class BindWidgetTest extends LauncherInstrumentationTestCase {
+
+    private ContentResolver mResolver;
+    private AppWidgetManagerCompat mWidgetManager;
+
+    // Objects created during test, which should be cleaned up in the end.
+    private Cursor mCursor;
+    // App install session id.
+    private int mSessionId = -1;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mResolver = mTargetContext.getContentResolver();
+        mWidgetManager = AppWidgetManagerCompat.getInstance(mTargetContext);
+        grantWidgetPermission();
+
+        // Clear all existing data
+        LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+        LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        if (mCursor != null) {
+            mCursor.close();
+        }
+
+        if (mSessionId > -1) {
+            mTargetContext.getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+        }
+    }
+
+    public void testBindNormalWidget_withConfig() {
+        LauncherAppWidgetProviderInfo info = findWidgetProvider(true);
+        LauncherAppWidgetInfo item = createWidgetInfo(info, true);
+
+        setupAndVerifyContents(item, LauncherAppWidgetHostView.class, info.label);
+    }
+
+    public void testBindNormalWidget_withoutConfig() {
+        LauncherAppWidgetProviderInfo info = findWidgetProvider(false);
+        LauncherAppWidgetInfo item = createWidgetInfo(info, true);
+
+        setupAndVerifyContents(item, LauncherAppWidgetHostView.class, info.label);
+    }
+
+    public void testUnboundWidget_removed() throws Exception {
+        LauncherAppWidgetProviderInfo info = findWidgetProvider(false);
+        LauncherAppWidgetInfo item = createWidgetInfo(info, false);
+        item.appWidgetId = -33;
+
+        // Since there is no widget to verify, just wait until the workspace is ready.
+        setupAndVerifyContents(item, Workspace.class, null);
+
+        waitUntilLoaderIdle();
+        // Item deleted from db
+        mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
+                null, null, null, null, null);
+        assertEquals(0, mCursor.getCount());
+
+        // The view does not exist
+        assertFalse(mDevice.findObject(new UiSelector().description(info.label)).exists());
+    }
+
+    public void testPendingWidget_autoRestored() {
+        // A non-restored widget with no config screen gets restored automatically.
+        LauncherAppWidgetProviderInfo info = findWidgetProvider(false);
+
+        // Do not bind the widget
+        LauncherAppWidgetInfo item = createWidgetInfo(info, false);
+        item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
+
+        setupAndVerifyContents(item, LauncherAppWidgetHostView.class, info.label);
+    }
+
+    public void testPendingWidget_withConfigScreen() throws Exception {
+        // A non-restored widget with config screen get bound and shows a 'Click to setup' UI.
+        LauncherAppWidgetProviderInfo info = findWidgetProvider(true);
+
+        // Do not bind the widget
+        LauncherAppWidgetInfo item = createWidgetInfo(info, false);
+        item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID;
+
+        setupAndVerifyContents(item, PendingAppWidgetHostView.class, null);
+        waitUntilLoaderIdle();
+        // Item deleted from db
+        mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
+                null, null, null, null, null);
+        mCursor.moveToNext();
+
+        // Widget has a valid Id now.
+        assertEquals(0, mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED))
+                & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
+        assertNotNull(mWidgetManager.getAppWidgetInfo(mCursor.getInt(mCursor.getColumnIndex(
+                LauncherSettings.Favorites.APPWIDGET_ID))));
+    }
+
+    public void testPendingWidget_notRestored_removed() throws Exception {
+        LauncherAppWidgetInfo item = getInvalidWidgetInfo();
+        item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
+                | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
+
+        setupAndVerifyContents(item, Workspace.class, null);
+        // The view does not exist
+        assertFalse(mDevice.findObject(
+                new UiSelector().className(PendingAppWidgetHostView.class)).exists());
+        waitUntilLoaderIdle();
+        // Item deleted from db
+        mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
+                null, null, null, null, null);
+        assertEquals(0, mCursor.getCount());
+    }
+
+    public void testPendingWidget_notRestored_brokenInstall() throws Exception {
+        // A widget which is was being installed once, even if its not being
+        // installed at the moment is not removed.
+        LauncherAppWidgetInfo item = getInvalidWidgetInfo();
+        item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
+                | LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
+                | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
+
+        setupAndVerifyContents(item, PendingAppWidgetHostView.class, null);
+        // Verify item still exists in db
+        waitUntilLoaderIdle();
+        mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
+                null, null, null, null, null);
+        assertEquals(1, mCursor.getCount());
+
+        // Widget still has an invalid id.
+        mCursor.moveToNext();
+        assertEquals(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID,
+                mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED))
+                        & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
+    }
+
+    public void testPendingWidget_notRestored_activeInstall() throws Exception {
+        // A widget which is being installed is not removed
+        LauncherAppWidgetInfo item = getInvalidWidgetInfo();
+        item.restoreStatus = LauncherAppWidgetInfo.FLAG_ID_NOT_VALID
+                | LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
+
+        // Create an active installer session
+        SessionParams params = new SessionParams(SessionParams.MODE_FULL_INSTALL);
+        params.setAppPackageName(item.providerName.getPackageName());
+        PackageInstaller installer = mTargetContext.getPackageManager().getPackageInstaller();
+        mSessionId = installer.createSession(params);
+
+        setupAndVerifyContents(item, PendingAppWidgetHostView.class, null);
+        // Verify item still exists in db
+        waitUntilLoaderIdle();
+        mCursor = mResolver.query(LauncherSettings.Favorites.getContentUri(item.id),
+                null, null, null, null, null);
+        assertEquals(1, mCursor.getCount());
+
+        // Widget still has an invalid id.
+        mCursor.moveToNext();
+        assertEquals(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID,
+                mCursor.getInt(mCursor.getColumnIndex(LauncherSettings.Favorites.RESTORED))
+                        & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
+    }
+
+    /**
+     * Adds {@param item} on the homescreen on the 0th screen at 0,0, and verifies that the
+     * widget class is displayed on the homescreen.
+     * @param widgetClass the View class which is displayed on the homescreen
+     * @param desc the content description of the view or null.
+     */
+    private void setupAndVerifyContents(
+            LauncherAppWidgetInfo item, Class<?> widgetClass, String desc) {
+        long screenId = Workspace.FIRST_SCREEN_ID;
+        // Update the screen id counter for the provider.
+        LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
+
+        if (screenId > Workspace.FIRST_SCREEN_ID) {
+            screenId = Workspace.FIRST_SCREEN_ID;
+        }
+        ContentValues v = new ContentValues();
+        v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
+        v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, 0);
+        mResolver.insert(LauncherSettings.WorkspaceScreens.CONTENT_URI, v);
+
+        // Insert the item
+        v = new ContentValues();
+        item.id = LauncherSettings.Settings.call(
+                mResolver, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
+                .getLong(LauncherSettings.Settings.EXTRA_VALUE);
+        item.screenId = screenId;
+        item.onAddToDatabase(mTargetContext, v);
+        v.put(LauncherSettings.Favorites._ID, item.id);
+        mResolver.insert(LauncherSettings.Favorites.CONTENT_URI, v);
+
+        // Reset loader
+        try {
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    LauncherClings.markFirstRunClingDismissed(mTargetContext);
+                    ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext);
+                    LauncherAppState.getInstance().getModel().resetLoadedState(true, true);
+                }
+            });
+        } catch (Throwable t) {
+            throw new IllegalArgumentException(t);
+        }
+        // Launch the home activity
+        startLauncher();
+        // Verify UI
+        UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
+                .className(widgetClass);
+        if (desc != null) {
+            selector = selector.description(desc);
+        }
+        assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
+    }
+
+    /**
+     * Creates a LauncherAppWidgetInfo corresponding to {@param info}
+     * @param bindWidget if true the info is bound and a valid widgetId is assigned to
+     *                   the LauncherAppWidgetInfo
+     */
+    private LauncherAppWidgetInfo createWidgetInfo(
+            LauncherAppWidgetProviderInfo info, boolean bindWidget) {
+        LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(
+                LauncherAppWidgetInfo.NO_ID, info.provider);
+        item.spanX = info.minSpanX;
+        item.spanY = info.minSpanY;
+        item.minSpanX = info.minSpanX;
+        item.minSpanY = info.minSpanY;
+        item.user = mWidgetManager.getUser(info);
+        item.cellX = 0;
+        item.cellY = 1;
+        item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
+
+        if (bindWidget) {
+            PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(mTargetContext, info);
+            pendingInfo.spanX = item.spanX;
+            pendingInfo.spanY = item.spanY;
+            pendingInfo.minSpanX = item.minSpanX;
+            pendingInfo.minSpanY = item.minSpanY;
+            Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(mTargetContext, pendingInfo);
+
+            AppWidgetHost host = new AppWidgetHost(mTargetContext, Launcher.APPWIDGET_HOST_ID);
+            int widgetId = host.allocateAppWidgetId();
+            if (!mWidgetManager.bindAppWidgetIdIfAllowed(widgetId, info, options)) {
+                host.deleteAppWidgetId(widgetId);
+                throw new IllegalArgumentException("Unable to bind widget id");
+            }
+            item.appWidgetId = widgetId;
+        }
+        return item;
+    }
+
+    /**
+     * Returns a LauncherAppWidgetInfo with package name which is not present on the device
+     */
+    private LauncherAppWidgetInfo getInvalidWidgetInfo() {
+        String invalidPackage = "com.invalidpackage";
+        int count = 0;
+        String pkg = invalidPackage;
+
+        Set<String> activePackage = getOnUiThread(new Callable<Set<String>>() {
+            @Override
+            public Set<String> call() throws Exception {
+                return PackageInstallerCompat.getInstance(mTargetContext)
+                        .updateAndGetActiveSessionCache().keySet();
+            }
+        });
+        while(true) {
+            try {
+                mTargetContext.getPackageManager().getPackageInfo(
+                        pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
+            } catch (Exception e) {
+                if (!activePackage.contains(pkg)) {
+                    break;
+                }
+            }
+            pkg = invalidPackage + count;
+            count ++;
+        }
+        LauncherAppWidgetInfo item = new LauncherAppWidgetInfo(10,
+                new ComponentName(pkg, "com.test.widgetprovider"));
+        item.spanX = 2;
+        item.spanY = 2;
+        item.minSpanX = 2;
+        item.minSpanY = 2;
+        item.cellX = 0;
+        item.cellY = 1;
+        item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
+        return item;
+    }
+
+    /**
+     * Blocks the current thread until all the jobs in the main worker thread are complete.
+     */
+    private void waitUntilLoaderIdle() throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        LauncherModel.sWorker.post(new Runnable() {
+            @Override
+            public void run() {
+                latch.countDown();
+            }
+        });
+        assertTrue(latch.await(5, TimeUnit.SECONDS));
+    }
+}
diff --git a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
index 21df601..230d623 100644
--- a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
+++ b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
@@ -41,7 +41,7 @@
     protected void setUp() throws Exception {
         super.setUp();
         mInvariantProfile = new InvariantDeviceProfile(getContext());
-        mPredefinedDeviceProfiles = mInvariantProfile.getPredefinedDeviceProfiles();
+        mPredefinedDeviceProfiles = mInvariantProfile.getPredefinedDeviceProfiles(getContext());
     }
 
     @Override
@@ -116,73 +116,4 @@
     // Add more tests for other devices, however, running them once on a single device is enough
     // for verifying that for a platform version, the WindowManager and DisplayMetrics is
     // working as intended.
-
-    /**
-     * Make sure that the height for the QSB is what we expect in normal mode.
-     */
-    public void testQsbNormalHeight() {
-        Resources resources = getContext().getResources();
-        DeviceProfile landscapeProfile = mInvariantProfile.landscapeProfile;
-        DeviceProfile portraitProfile = mInvariantProfile.portraitProfile;
-        landscapeProfile.setSearchBarHeight(LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL);
-        portraitProfile.setSearchBarHeight(LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL);
-        Rect portraitBounds = portraitProfile.getSearchBarBounds(true); // RTL shouldn't matter.
-        int portraitHeight = (int) Utilities.dpiFromPx(portraitBounds.height(),
-                resources.getDisplayMetrics());
-        Rect landscapeBounds = landscapeProfile.getSearchBarBounds(true); // RTL shouldn't matter.
-        int landscapeHeight = (int) Utilities.dpiFromPx(landscapeBounds.height(),
-                resources.getDisplayMetrics());
-        if (portraitProfile.isTablet) {
-            assertEquals(8 + 48 + 24, portraitHeight);
-        } else {
-            assertEquals(8 + 48 + 12, portraitHeight);
-        }
-        // Make sure the height that we pass in the widget options bundle is the height of the
-        // search bar + 8dps padding top and bottom.
-        Point portraitDimens = portraitProfile.getSearchBarDimensForWidgetOpts(resources);
-        int portraitWidgetOptsHeight = portraitDimens.y;
-        Point landscapeDimens = landscapeProfile.getSearchBarDimensForWidgetOpts(resources);
-        int landscapeWidgetOptsHeight = landscapeDimens.y;
-        assertEquals(8 + 48 + 8, (int) Utilities.dpiFromPx(portraitWidgetOptsHeight,
-                resources.getDisplayMetrics()));
-        if (!landscapeProfile.isVerticalBarLayout()) {
-            assertEquals(portraitHeight, landscapeHeight);
-            assertEquals(portraitWidgetOptsHeight, landscapeWidgetOptsHeight);
-        }
-    }
-
-    /**
-     * Make sure that the height for the QSB is what we expect in tall mode.
-     */
-    public void testQsbTallHeight() {
-        Resources resources = getContext().getResources();
-        DeviceProfile landscapeProfile = mInvariantProfile.landscapeProfile;
-        DeviceProfile portraitProfile = mInvariantProfile.portraitProfile;
-        landscapeProfile.setSearchBarHeight(LauncherCallbacks.SEARCH_BAR_HEIGHT_TALL);
-        portraitProfile.setSearchBarHeight(LauncherCallbacks.SEARCH_BAR_HEIGHT_TALL);
-        Rect portraitBounds = portraitProfile.getSearchBarBounds(true); // RTL shouldn't matter.
-        int portraitHeight = (int) Utilities.dpiFromPx(portraitBounds.height(),
-                resources.getDisplayMetrics());
-        Rect landscapeBounds = landscapeProfile.getSearchBarBounds(true); // RTL shouldn't matter.
-        int landscapeHeight = (int) Utilities.dpiFromPx(landscapeBounds.height(),
-                resources.getDisplayMetrics());
-        if (portraitProfile.isPhone) {
-            // This fails on some devices due to http://b/26884580 (portraitHeight is 101, not 100).
-            assertEquals(4 + 94 + 2, portraitHeight);
-        } else {
-            assertEquals(8 + 94 + 24, portraitHeight);
-        }
-        // Make sure the height that we pass in the widget options bundle is the height of the
-        // search bar + 8dps padding top and bottom.
-        Point portraitDimens = portraitProfile.getSearchBarDimensForWidgetOpts(resources);
-        int portraitWidgetOptsHeight = portraitDimens.y;
-        Point landscapeDimens = landscapeProfile.getSearchBarDimensForWidgetOpts(resources);
-        int landscapeWidgetOptsHeight = landscapeDimens.y;
-        assertEquals(8 + 94 + 8, (int) Utilities.dpiFromPx(portraitWidgetOptsHeight,
-                resources.getDisplayMetrics()));
-        if (!landscapeProfile.isVerticalBarLayout()) {
-            assertEquals(portraitHeight, landscapeHeight);
-            assertEquals(portraitWidgetOptsHeight, landscapeWidgetOptsHeight);
-        }
-    }
 }
diff --git a/tests/src/com/android/launcher3/QuickAddWidgetTest.java b/tests/src/com/android/launcher3/QuickAddWidgetTest.java
deleted file mode 100644
index 027c465..0000000
--- a/tests/src/com/android/launcher3/QuickAddWidgetTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package com.android.launcher3;
-
-import android.content.Intent;
-import android.graphics.Point;
-import android.os.SystemClock;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.Direction;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
-import android.test.InstrumentationTestCase;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-import java.util.List;
-
-/**
- * Add an arbitrary widget from the widget picker very quickly to test potential race conditions.
- */
-public class QuickAddWidgetTest extends InstrumentationTestCase {
-    // Disabled because it's flaky and not particularly useful. But this class could still be useful
-    // as an example if we want other UI tests in the future.
-    private static final boolean DISABLED = true;
-
-    private UiDevice mDevice;
-    private String mTargetPackage;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mDevice = UiDevice.getInstance(getInstrumentation());
-
-        // Set Launcher3 as home.
-        mTargetPackage = getInstrumentation().getTargetContext().getPackageName();
-        Intent homeIntent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .setPackage(mTargetPackage)
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        getInstrumentation().getContext().startActivity(homeIntent);
-        mDevice.wait(Until.hasObject(By.pkg(mTargetPackage).depth(0)), 3000);
-    }
-
-    public void testAddWidgetQuickly() throws Exception {
-        if (DISABLED) return;
-        mDevice.pressMenu(); // Enter overview mode.
-        mDevice.wait(Until.findObject(By.text("Widgets")), 3000).click();
-        UiObject2 calendarWidget = getWidgetByName("Clock");
-        Point center = calendarWidget.getVisibleCenter();
-        // Touch widget just long enough to pick it up (longPressTimeout), then let go immediately.
-        getInstrumentation().sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(),
-                SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, center.x, center.y, 0));
-        Thread.sleep(ViewConfiguration.getLongPressTimeout() + 50);
-        getInstrumentation().sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(),
-                SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, center.x, center.y, 0));
-
-        assertTrue("Drag was never started", isOnHomescreen());
-    }
-
-    private UiObject2 getWidgetByName(String name) {
-        UiObject2 widgetsList = mDevice.wait(Until.findObject(By.res(mTargetPackage,
-                "widgets_list_view")), 3000);
-        do {
-            UiObject2 widget = getVisibleWidgetByName(name);
-            if (widget != null) {
-                return widget;
-            }
-        } while (widgetsList.scroll(Direction.DOWN, 1f));
-        return getVisibleWidgetByName(name);
-    }
-
-    private UiObject2 getVisibleWidgetByName(String name) {
-        List<UiObject2> visibleWidgets = mDevice.wait(Until.findObjects(By.clazz(
-                "android.widget.LinearLayout")), 3000);
-        for (UiObject2 widget : visibleWidgets) {
-            if (widget.hasObject(By.text(name))) {
-                return widget;
-            }
-        }
-        return null;
-    }
-
-    private boolean isOnHomescreen() {
-        return mDevice.wait(Until.hasObject(By.res(mTargetPackage, "hotseat")), 3000);
-    }
-}
diff --git a/tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java b/tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java
new file mode 100644
index 0000000..18570de
--- /dev/null
+++ b/tests/src/com/android/launcher3/allapps/DefaultAppSearchAlgorithmTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps;
+
+import android.content.ComponentName;
+import android.test.InstrumentationTestCase;
+
+import com.android.launcher3.AppInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit tests for {@link DefaultAppSearchAlgorithm}
+ */
+public class DefaultAppSearchAlgorithmTest extends InstrumentationTestCase {
+
+    private List<AppInfo> mAppsList;
+    private DefaultAppSearchAlgorithm mAlgorithm;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mAppsList = new ArrayList<>();
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mAlgorithm = new DefaultAppSearchAlgorithm(mAppsList);
+            }
+        });
+    }
+
+    public void testMatches() {
+        assertTrue(mAlgorithm.matches(getInfo("white cow"), "cow"));
+        assertTrue(mAlgorithm.matches(getInfo("whiteCow"), "cow"));
+        assertTrue(mAlgorithm.matches(getInfo("whiteCOW"), "cow"));
+        assertTrue(mAlgorithm.matches(getInfo("whitecowCOW"), "cow"));
+        assertTrue(mAlgorithm.matches(getInfo("white2cow"), "cow"));
+
+        assertFalse(mAlgorithm.matches(getInfo("whitecow"), "cow"));
+        assertFalse(mAlgorithm.matches(getInfo("whitEcow"), "cow"));
+
+        assertTrue(mAlgorithm.matches(getInfo("whitecowCow"), "cow"));
+        assertTrue(mAlgorithm.matches(getInfo("whitecow cow"), "cow"));
+        assertFalse(mAlgorithm.matches(getInfo("whitecowcow"), "cow"));
+        assertFalse(mAlgorithm.matches(getInfo("whit ecowcow"), "cow"));
+
+        assertTrue(mAlgorithm.matches(getInfo("cats&dogs"), "dog"));
+        assertTrue(mAlgorithm.matches(getInfo("cats&Dogs"), "dog"));
+        assertTrue(mAlgorithm.matches(getInfo("cats&Dogs"), "&"));
+
+        assertTrue(mAlgorithm.matches(getInfo("2+43"), "43"));
+        assertFalse(mAlgorithm.matches(getInfo("2+43"), "3"));
+
+        assertTrue(mAlgorithm.matches(getInfo("Q"), "q"));
+        assertTrue(mAlgorithm.matches(getInfo("  Q"), "q"));
+
+        // match lower case words
+        assertTrue(mAlgorithm.matches(getInfo("elephant"), "e"));
+
+    }
+
+    private AppInfo getInfo(String title) {
+        AppInfo info = new AppInfo();
+        info.title = title;
+        info.componentName = new ComponentName("Test", title);
+        return info;
+    }
+}
diff --git a/tests/src/com/android/launcher3/logging/FileLogTest.java b/tests/src/com/android/launcher3/logging/FileLogTest.java
new file mode 100644
index 0000000..c24cc3f
--- /dev/null
+++ b/tests/src/com/android/launcher3/logging/FileLogTest.java
@@ -0,0 +1,77 @@
+package com.android.launcher3.logging;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Calendar;
+
+/**
+ * Tests for {@link FileLog}
+ */
+@SmallTest
+public class FileLogTest extends AndroidTestCase {
+
+    private File mTempDir;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        int count = 0;
+        do {
+            mTempDir = new File(getContext().getCacheDir(), "log-test-" + (count++));
+        } while(!mTempDir.mkdir());
+
+        FileLog.setDir(mTempDir);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // Clear existing logs
+        new File(mTempDir, "log-0").delete();
+        new File(mTempDir, "log-1").delete();
+        mTempDir.delete();
+        super.tearDown();
+    }
+
+    public void testPrintLog() throws Exception {
+        FileLog.print("Testing", "hoolalala");
+        StringWriter writer = new StringWriter();
+        FileLog.flushAll(new PrintWriter(writer));
+        assertTrue(writer.toString().contains("hoolalala"));
+
+        FileLog.print("Testing", "abracadabra", new Exception("cat! cat!"));
+        writer = new StringWriter();
+        FileLog.flushAll(new PrintWriter(writer));
+        assertTrue(writer.toString().contains("abracadabra"));
+        // Exception is also printed
+        assertTrue(writer.toString().contains("cat! cat!"));
+
+        // Old logs still present after flush
+        assertTrue(writer.toString().contains("hoolalala"));
+    }
+
+    public void testOldFileTruncated() throws Exception {
+        FileLog.print("Testing", "hoolalala");
+        StringWriter writer = new StringWriter();
+        FileLog.flushAll(new PrintWriter(writer));
+        assertTrue(writer.toString().contains("hoolalala"));
+
+        Calendar threeDaysAgo = Calendar.getInstance();
+        threeDaysAgo.add(Calendar.HOUR, -72);
+        new File(mTempDir, "log-0").setLastModified(threeDaysAgo.getTimeInMillis());
+        new File(mTempDir, "log-1").setLastModified(threeDaysAgo.getTimeInMillis());
+
+        FileLog.print("Testing", "abracadabra", new Exception("cat! cat!"));
+        writer = new StringWriter();
+        FileLog.flushAll(new PrintWriter(writer));
+        assertTrue(writer.toString().contains("abracadabra"));
+        // Exception is also printed
+        assertTrue(writer.toString().contains("cat! cat!"));
+
+        // Old logs have been truncated
+        assertFalse(writer.toString().contains("hoolalala"));
+    }
+}
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
index ec157bc..fc7fe48 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -1,25 +1,29 @@
 package com.android.launcher3.model;
 
 import android.content.ContentValues;
+import android.content.Context;
 import android.content.Intent;
 import android.database.Cursor;
 import android.graphics.Point;
 import android.test.ProviderTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
 
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
 import com.android.launcher3.util.TestLauncherProvider;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 
 /**
  * Unit tests for {@link GridSizeMigrationTask}
  */
+@MediumTest
 public class GridSizeMigrationTaskTest extends ProviderTestCase2<TestLauncherProvider> {
 
     private static final long DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP;
@@ -57,10 +61,16 @@
                 addItem(APPLICATION, 4, HOTSEAT, 0, 0),
         };
 
-        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 2, 3, 1)
+        mIdp.numHotseatIcons = 3;
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 3)
                 .migrateHotseat();
-        // First & last items are dropped as they have the least weight.
-        verifyHotseat(hotseatItems[1], -1, hotseatItems[3]);
+        if (FeatureFlags.NO_ALL_APPS_ICON) {
+            // First item is dropped as it has the least weight.
+            verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
+        } else {
+            // First & last items are dropped as they have the least weight.
+            verifyHotseat(hotseatItems[1], -1, hotseatItems[3]);
+        }
     }
 
     public void testHotseatMigration_shortcuts_dropped() throws Exception {
@@ -72,10 +82,16 @@
                 addItem(10, 4, HOTSEAT, 0, 0),
         };
 
-        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 2, 3, 1)
+        mIdp.numHotseatIcons = 3;
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 3)
                 .migrateHotseat();
-        // First & third items are dropped as they have the least weight.
-        verifyHotseat(hotseatItems[1], -1, hotseatItems[4]);
+        if (FeatureFlags.NO_ALL_APPS_ICON) {
+            // First item is dropped as it has the least weight.
+            verifyHotseat(hotseatItems[1], hotseatItems[3], hotseatItems[4]);
+        } else {
+            // First & third items are dropped as they have the least weight.
+            verifyHotseat(hotseatItems[1], -1, hotseatItems[4]);
+        }
     }
 
     private void verifyHotseat(long... sortedIds) {
@@ -116,7 +132,7 @@
                 {  5,  2, -1,  6},
         }});
 
-        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages,
                 new Point(4, 4), new Point(3, 3)).migrateWorkspace();
 
         // Column 2 and row 2 got removed.
@@ -135,7 +151,7 @@
                 {  5,  2, -1,  6},
         }});
 
-        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages,
                 new Point(4, 4), new Point(3, 3)).migrateWorkspace();
 
         // Items in the second column get moved to new screen
@@ -159,7 +175,7 @@
                 {  3,  1, -1,  4},
         }});
 
-        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages,
                 new Point(4, 4), new Point(3, 3)).migrateWorkspace();
 
         // Items in the second column of the first screen should get placed on the 3rd
@@ -190,7 +206,7 @@
                 {  5,  2, -1,  6},
         }});
 
-        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages,
                 new Point(4, 4), new Point(3, 3)).migrateWorkspace();
 
         // Items in the second column of the first screen should get placed on a new screen.
@@ -207,6 +223,57 @@
         }});
     }
 
+    public void testWorkspace_first_row_blocked() throws Exception {
+        // The first screen has one item on the 4th column which needs moving, as the first row
+        // will be kept empty.
+        long[][][] ids = createGrid(new int[][][]{{
+                { -1, -1, -1, -1},
+                {  3,  1,  7,  0},
+                {  8,  7,  7, -1},
+                {  5,  2,  7, -1},
+        }}, 0);
+
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages,
+                new Point(4, 4), new Point(3, 4)).migrateWorkspace();
+
+        // Items in the second column of the first screen should get placed on a new screen.
+        verifyWorkspace(new long[][][] {{
+                {          -1,           -1,           -1},
+                {ids[0][1][0], ids[0][1][1], ids[0][1][2]},
+                {ids[0][2][0], ids[0][2][1], ids[0][2][2]},
+                {ids[0][3][0], ids[0][3][1], ids[0][3][2]},
+        }, {
+                {ids[0][1][3]},
+        }});
+    }
+
+    public void testWorkspace_items_moved_to_empty_first_row() throws Exception {
+        // Items will get moved to the next screen to keep the first screen empty.
+        long[][][] ids = createGrid(new int[][][]{{
+                { -1, -1, -1, -1},
+                {  0,  1,  0,  0},
+                {  8,  7,  7, -1},
+                {  5,  6,  7, -1},
+        }}, 0);
+
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages,
+                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+        // Items in the second column of the first screen should get placed on a new screen.
+        verifyWorkspace(new long[][][] {{
+                {          -1,           -1,           -1},
+                {ids[0][2][0], ids[0][2][1], ids[0][2][2]},
+                {ids[0][3][0], ids[0][3][1], ids[0][3][2]},
+        }, {
+                {ids[0][1][1], ids[0][1][0], ids[0][1][2]},
+                {ids[0][1][3]},
+        }});
+    }
+
+    private long[][][] createGrid(int[][][] typeArray) throws Exception {
+        return createGrid(typeArray, 1);
+    }
+
     /**
      * Initializes the DB with dummy elements to represent the provided grid structure.
      * @param typeArray A 3d array of item types. {@see #addItem(int, long, long, int, int)} for
@@ -214,12 +281,19 @@
      *                  two represent the workspace grid.
      * @return the same grid representation where each entry is the corresponding item id.
      */
-    private long[][][] createGrid(int[][][] typeArray) throws Exception {
+    private long[][][] createGrid(int[][][] typeArray, long startScreen) throws Exception {
+        LauncherSettings.Settings.call(getMockContentResolver(),
+                LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
         long[][][] ids = new long[typeArray.length][][];
 
         for (int i = 0; i < typeArray.length; i++) {
             // Add screen to DB
-            long screenId = LauncherAppState.getLauncherProvider().generateNewScreenId();
+            long screenId = startScreen + i;
+
+            // Keep the screen id counter up to date
+            LauncherSettings.Settings.call(getMockContentResolver(),
+                    LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
+
             ContentValues v = new ContentValues();
             v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
             v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
@@ -266,7 +340,8 @@
                     } else {
                         assertEquals(1, c.getCount());
                         c.moveToNext();
-                        assertEquals(id, c.getLong(0));
+                        assertEquals(String.format("Failed to verify item at %d %d, %d", i, y, x),
+                                id, c.getLong(0));
                         total++;
                     }
                     c.close();
@@ -288,7 +363,9 @@
      *             folder (where the type represents the number of items in the folder).
      */
     private long addItem(int type, long screen, long container, int x, int y) throws Exception {
-        long id = LauncherAppState.getLauncherProvider().generateNewItemId();
+        long id = LauncherSettings.Settings.call(getMockContentResolver(),
+                LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
+                .getLong(LauncherSettings.Settings.EXTRA_VALUE);
 
         ContentValues values = new ContentValues();
         values.put(LauncherSettings.Favorites._ID, id);
@@ -314,4 +391,53 @@
         getMockContentResolver().insert(LauncherSettings.Favorites.CONTENT_URI, values);
         return id;
     }
+
+    public void testMultiStepMigration_small_to_large() throws Exception {
+        MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier();
+        verifier.migrate(new Point(3, 3), new Point(5, 5));
+        verifier.assertCompleted();
+    }
+
+    public void testMultiStepMigration_large_to_small() throws Exception {
+        MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier(
+                5, 5, 4, 4,
+                4, 4, 3, 4
+        );
+        verifier.migrate(new Point(5, 5), new Point(3, 4));
+        verifier.assertCompleted();
+    }
+
+    public void testMultiStepMigration_zig_zag() throws Exception {
+        MultiStepMigrationTaskVerifier verifier = new MultiStepMigrationTaskVerifier(
+                5, 7, 4, 7,
+                4, 7, 3, 7
+        );
+        verifier.migrate(new Point(5, 5), new Point(3, 7));
+        verifier.assertCompleted();
+    }
+
+    private static class MultiStepMigrationTaskVerifier extends MultiStepMigrationTask {
+
+        private final LinkedList<Point> mPoints;
+
+        public MultiStepMigrationTaskVerifier(int... points) {
+            super(null, null);
+
+            mPoints = new LinkedList<>();
+            for (int i = 0; i < points.length; i += 2) {
+                mPoints.add(new Point(points[i], points[i + 1]));
+            }
+        }
+
+        @Override
+        protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
+            assertEquals(sourceSize, mPoints.poll());
+            assertEquals(nextSize, mPoints.poll());
+            return false;
+        }
+
+        public void assertCompleted() {
+            assertTrue(mPoints.isEmpty());
+        }
+    }
 }
diff --git a/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
new file mode 100644
index 0000000..29f738b
--- /dev/null
+++ b/tests/src/com/android/launcher3/provider/RestoreDbTaskTest.java
@@ -0,0 +1,74 @@
+package com.android.launcher3.provider;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.android.launcher3.LauncherProvider.DatabaseHelper;
+import com.android.launcher3.LauncherSettings.Favorites;
+
+/**
+ * Tests for {@link RestoreDbTask}
+ */
+@MediumTest
+public class RestoreDbTaskTest extends AndroidTestCase {
+
+    public void testGetProfileId() throws Exception {
+        SQLiteDatabase db = new MyDatabaseHelper(23).getWritableDatabase();
+        assertEquals(23, new RestoreDbTask().getDefaultProfileId(db));
+    }
+
+    public void testMigrateProfileId() throws Exception {
+        SQLiteDatabase db = new MyDatabaseHelper(42).getWritableDatabase();
+        // Add some dummy data
+        for (int i = 0; i < 5; i++) {
+            ContentValues values = new ContentValues();
+            values.put(Favorites._ID, i);
+            values.put(Favorites.TITLE, "item " + i);
+            db.insert(Favorites.TABLE_NAME, null, values);
+        }
+        // Verify item add
+        assertEquals(5, getCount(db, "select * from favorites where profileId = 42"));
+
+        new RestoreDbTask().migrateProfileId(db, 33);
+
+        // verify data migrated
+        assertEquals(0, getCount(db, "select * from favorites where profileId = 42"));
+        assertEquals(5, getCount(db, "select * from favorites where profileId = 33"));
+
+        // Verify default value changed
+        ContentValues values = new ContentValues();
+        values.put(Favorites._ID, 100);
+        values.put(Favorites.TITLE, "item 100");
+        db.insert(Favorites.TABLE_NAME, null, values);
+        assertEquals(6, getCount(db, "select * from favorites where profileId = 33"));
+    }
+
+    private int getCount(SQLiteDatabase db, String sql) {
+        Cursor c = db.rawQuery(sql, null);
+        try {
+            return c.getCount();
+        } finally {
+            c.getCount();
+        }
+    }
+
+    private class MyDatabaseHelper extends DatabaseHelper {
+
+        private final long mProfileId;
+
+        public MyDatabaseHelper(long profileId) {
+            super(getContext(), null, null);
+            mProfileId = profileId;
+        }
+
+        @Override
+        public long getDefaultUserSerial() {
+            return mProfileId;
+        }
+
+        protected void onEmptyDbCreated() { }
+    }
+}
diff --git a/tests/src/com/android/launcher3/shortcuts/ShortcutFilterTest.java b/tests/src/com/android/launcher3/shortcuts/ShortcutFilterTest.java
new file mode 100644
index 0000000..05d0ffb
--- /dev/null
+++ b/tests/src/com/android/launcher3/shortcuts/ShortcutFilterTest.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.shortcuts;
+
+import android.content.pm.ShortcutInfo;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static com.android.launcher3.shortcuts.ShortcutFilter.MAX_SHORTCUTS;
+import static com.android.launcher3.shortcuts.ShortcutFilter.NUM_DYNAMIC;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests the sorting and filtering of shortcuts in {@link ShortcutFilter}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ShortcutFilterTest {
+
+    @Test
+    public void testSortAndFilterShortcuts() {
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 0), 3, 0);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 3), 0, 3);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 0), MAX_SHORTCUTS, 0);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(0, 5), 0, MAX_SHORTCUTS);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 3),
+                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 5),
+                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 1), MAX_SHORTCUTS - 1, 1);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(1, 5), 1, MAX_SHORTCUTS - 1);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(5, 3),
+                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
+        filterShortcutsAndAssertNumStaticAndDynamic(createShortcutsList(3, 5),
+                MAX_SHORTCUTS - NUM_DYNAMIC, NUM_DYNAMIC);
+    }
+
+    private void filterShortcutsAndAssertNumStaticAndDynamic(
+            List<ShortcutInfoCompat> shortcuts, int expectedStatic, int expectedDynamic) {
+        Collections.shuffle(shortcuts);
+        List<ShortcutInfoCompat> filteredShortcuts = ShortcutFilter.sortAndFilterShortcuts(shortcuts);
+        assertIsSorted(filteredShortcuts);
+
+        int numStatic = 0;
+        int numDynamic = 0;
+        for (ShortcutInfoCompat shortcut : filteredShortcuts) {
+            if (shortcut.isDeclaredInManifest()) {
+                numStatic++;
+            }
+            if (shortcut.isDynamic()) {
+                numDynamic++;
+            }
+        }
+        assertEquals(expectedStatic, numStatic);
+        assertEquals(expectedDynamic, numDynamic);
+    }
+
+    private void assertIsSorted(List<ShortcutInfoCompat> shortcuts) {
+        int lastStaticRank = -1;
+        int lastDynamicRank = -1;
+        boolean hasSeenDynamic = false;
+        for (ShortcutInfoCompat shortcut : shortcuts) {
+            int rank = shortcut.getRank();
+            if (shortcut.isDeclaredInManifest()) {
+                assertFalse("Static shortcuts should come before all dynamic shortcuts.",
+                        hasSeenDynamic);
+                assertTrue(rank > lastStaticRank);
+                lastStaticRank = rank;
+            }
+            if (shortcut.isDynamic()) {
+                hasSeenDynamic = true;
+                assertTrue(rank > lastDynamicRank);
+                lastDynamicRank = rank;
+            }
+        }
+    }
+
+    private List<ShortcutInfoCompat> createShortcutsList(int numStatic, int numDynamic) {
+        List<ShortcutInfoCompat> shortcuts = new ArrayList<>();
+        for (int i = 0; i < numStatic; i++) {
+            shortcuts.add(new Shortcut(true, i));
+        }
+        for (int i = 0; i < numDynamic; i++) {
+            shortcuts.add(new Shortcut(false, i));
+        }
+        return shortcuts;
+    }
+
+    private class Shortcut extends ShortcutInfoCompat {
+        private boolean mIsStatic;
+        private int mRank;
+
+        public Shortcut(ShortcutInfo shortcutInfo) {
+            super(shortcutInfo);
+        }
+
+        public Shortcut(boolean isStatic, int rank) {
+            this(null);
+            mIsStatic = isStatic;
+            mRank = rank;
+        }
+
+        @Override
+        public boolean isDeclaredInManifest() {
+            return mIsStatic;
+        }
+
+        @Override
+        public boolean isDynamic() {
+            return !mIsStatic;
+        }
+
+        @Override
+        public int getRank() {
+            return mRank;
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/ui/AddWidgetTest.java b/tests/src/com/android/launcher3/ui/AddWidgetTest.java
new file mode 100644
index 0000000..a0ca60c
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/AddWidgetTest.java
@@ -0,0 +1,64 @@
+package com.android.launcher3.ui;
+
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiObject2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.View;
+
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.util.Condition;
+import com.android.launcher3.util.Wait;
+import com.android.launcher3.widget.WidgetCell;
+
+/**
+ * Test to add widget from widget tray
+ */
+@LargeTest
+public class AddWidgetTest extends LauncherInstrumentationTestCase {
+
+    private LauncherAppWidgetProviderInfo widgetInfo;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        widgetInfo = findWidgetProvider(false /* hasConfigureScreen */);
+    }
+
+    public void testDragIcon_portrait() throws Throwable {
+        lockRotation(true);
+        performTest();
+    }
+
+    public void testDragIcon_landscape() throws Throwable {
+        lockRotation(false);
+        performTest();
+    }
+
+    private void performTest() throws Throwable {
+        clearHomescreen();
+        Launcher launcher = startLauncher();
+
+        // Open widget tray and wait for load complete.
+        final UiObject2 widgetContainer = openWidgetsTray();
+        assertTrue(Wait.atMost(Condition.minChildCount(widgetContainer, 2), DEFAULT_UI_TIMEOUT));
+
+        // Drag widget to homescreen
+        UiObject2 widget = scrollAndFind(widgetContainer, By.clazz(WidgetCell.class)
+                .hasDescendant(By.text(widgetInfo.getLabel(mTargetContext.getPackageManager()))));
+        dragToWorkspace(widget);
+
+        assertNotNull(launcher.getWorkspace().getFirstMatch(new ItemOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View view) {
+                return info instanceof LauncherAppWidgetInfo &&
+                        ((LauncherAppWidgetInfo) info).providerName.equals(widgetInfo.provider);
+            }
+        }));
+    }
+}
diff --git a/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java b/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java
new file mode 100644
index 0000000..abe6b95
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/AllAppsAppLaunchTest.java
@@ -0,0 +1,52 @@
+package com.android.launcher3.ui;
+
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.Condition;
+import com.android.launcher3.util.Wait;
+
+/**
+ * Test for verifying apps is launched from all-apps
+ */
+@LargeTest
+public class AllAppsAppLaunchTest extends LauncherInstrumentationTestCase {
+
+    private LauncherActivityInfoCompat mSettingsApp;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
+                .getActivityList("com.android.settings", UserHandleCompat.myUserHandle()).get(0);
+    }
+
+    public void testAppLauncher_portrait() throws Exception {
+        lockRotation(true);
+        performTest();
+    }
+
+    public void testAppLauncher_landscape() throws Exception {
+        lockRotation(false);
+        performTest();
+    }
+
+    private void performTest() throws Exception {
+        startLauncher();
+
+        // Open all apps and wait for load complete
+        final UiObject2 appsContainer = openAllApps();
+        assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
+
+        // Open settings app and verify app launched
+        scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString())).click();
+        assertTrue(mDevice.wait(Until.hasObject(By.pkg(
+                mSettingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT));
+    }
+}
diff --git a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
new file mode 100644
index 0000000..56fc90a
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
@@ -0,0 +1,57 @@
+package com.android.launcher3.ui;
+
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.Condition;
+import com.android.launcher3.util.Wait;
+
+/**
+ * Test for dragging an icon from all-apps to homescreen.
+ */
+@LargeTest
+public class AllAppsIconToHomeTest extends LauncherInstrumentationTestCase {
+
+    private LauncherActivityInfoCompat mSettingsApp;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mSettingsApp = LauncherAppsCompat.getInstance(mTargetContext)
+                .getActivityList("com.android.settings", UserHandleCompat.myUserHandle()).get(0);
+    }
+
+    public void testDragIcon_portrait() throws Throwable {
+        lockRotation(true);
+        performTest();
+    }
+
+    public void testDragIcon_landscape() throws Throwable {
+        lockRotation(false);
+        performTest();
+    }
+
+    private void performTest() throws Throwable {
+        clearHomescreen();
+        startLauncher();
+
+        // Open all apps and wait for load complete.
+        final UiObject2 appsContainer = openAllApps();
+        assertTrue(Wait.atMost(Condition.minChildCount(appsContainer, 2), DEFAULT_UI_TIMEOUT));
+
+        // Drag icon to homescreen.
+        UiObject2 icon = scrollAndFind(appsContainer, By.text(mSettingsApp.getLabel().toString()));
+        dragToWorkspace(icon);
+
+        // Verify that the icon works on homescreen.
+        mDevice.findObject(By.text(mSettingsApp.getLabel().toString())).click();
+        assertTrue(mDevice.wait(Until.hasObject(By.pkg(
+                mSettingsApp.getComponentName().getPackageName()).depth(0)), DEFAULT_UI_TIMEOUT));
+    }
+}
diff --git a/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
new file mode 100644
index 0000000..e858d17
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/LauncherInstrumentationTestCase.java
@@ -0,0 +1,273 @@
+package com.android.launcher3.ui;
+
+import android.app.SearchManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Point;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.Direction;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.InstrumentationTestCase;
+import android.view.MotionEvent;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherClings;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.ManagedProfileHeuristic;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Locale;
+import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Base class for all instrumentation tests providing various utility methods.
+ */
+public class LauncherInstrumentationTestCase extends InstrumentationTestCase {
+
+    public static final long DEFAULT_UI_TIMEOUT = 3000;
+
+    protected UiDevice mDevice;
+    protected Context mTargetContext;
+    protected String mTargetPackage;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mDevice = UiDevice.getInstance(getInstrumentation());
+        mTargetContext = getInstrumentation().getTargetContext();
+        mTargetPackage = mTargetContext.getPackageName();
+    }
+
+    protected void lockRotation(boolean naturalOrientation) throws RemoteException {
+        Utilities.getPrefs(mTargetContext)
+                .edit()
+                .putBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, !naturalOrientation)
+                .commit();
+
+        if (naturalOrientation) {
+            mDevice.setOrientationNatural();
+        } else {
+            mDevice.setOrientationRight();
+        }
+    }
+
+    /**
+     * Starts the launcher activity in the target package and returns the Launcher instance.
+     */
+    protected Launcher startLauncher() {
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setPackage(mTargetPackage)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return (Launcher) getInstrumentation().startActivitySync(homeIntent);
+    }
+
+    /**
+     * Grants the launcher permission to bind widgets.
+     */
+    protected void grantWidgetPermission() throws IOException {
+        // Check bind widget permission
+        if (mTargetContext.getPackageManager().checkPermission(
+                mTargetPackage, android.Manifest.permission.BIND_APPWIDGET)
+                != PackageManager.PERMISSION_GRANTED) {
+            ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand(
+                    "appwidget grantbind --package " + mTargetPackage);
+            // Read the input stream fully.
+            FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+            while (fis.read() != -1);
+            fis.close();
+        }
+    }
+
+    /**
+     * Opens all apps and returns the recycler view
+     */
+    protected UiObject2 openAllApps() {
+        if (FeatureFlags.LAUNCHER3_ALL_APPS_PULL_UP) {
+            // clicking on the page indicator brings up all apps tray on non tablets.
+            findViewById(R.id.page_indicator).click();
+        } else {
+            mDevice.wait(Until.findObject(
+                    By.desc(mTargetContext.getString(R.string.all_apps_button_label))),
+                    DEFAULT_UI_TIMEOUT).click();
+        }
+        return findViewById(R.id.apps_list_view);
+    }
+
+    /**
+     * Opens widget tray and returns the recycler view.
+     */
+    protected UiObject2 openWidgetsTray() {
+        mDevice.pressMenu(); // Enter overview mode.
+        mDevice.wait(Until.findObject(
+                By.text(mTargetContext.getString(R.string.widget_button_text)
+                        .toUpperCase(Locale.getDefault()))), DEFAULT_UI_TIMEOUT).click();
+        return findViewById(R.id.widgets_list_view);
+    }
+
+    /**
+     * Scrolls the {@param container} until it finds an object matching {@param condition}.
+     * @return the matching object.
+     */
+    protected UiObject2 scrollAndFind(UiObject2 container, BySelector condition) {
+        do {
+            UiObject2 widget = container.findObject(condition);
+            if (widget != null) {
+                return widget;
+            }
+        } while (container.scroll(Direction.DOWN, 1f));
+        return container.findObject(condition);
+    }
+
+    /**
+     * Drags an icon to the center of homescreen.
+     */
+    protected void dragToWorkspace(UiObject2 icon) {
+        Point center = icon.getVisibleCenter();
+
+        // Action Down
+        sendPointer(MotionEvent.ACTION_DOWN, center);
+
+        // Wait until "Remove/Delete target is visible
+        assertNotNull(findViewById(R.id.delete_target_text));
+
+        Point moveLocation = findViewById(R.id.drag_layer).getVisibleCenter();
+
+        // Move to center
+        while(!moveLocation.equals(center)) {
+            center.x = getNextMoveValue(moveLocation.x, center.x);
+            center.y = getNextMoveValue(moveLocation.y, center.y);
+            sendPointer(MotionEvent.ACTION_MOVE, center);
+        }
+        sendPointer(MotionEvent.ACTION_UP, center);
+
+        // Wait until remove target is gone.
+        mDevice.wait(Until.gone(getSelectorForId(R.id.delete_target_text)), DEFAULT_UI_TIMEOUT);
+    }
+
+    private int getNextMoveValue(int targetValue, int oldValue) {
+        if (targetValue - oldValue > 10) {
+            return oldValue + 10;
+        } else if (targetValue - oldValue < -10) {
+            return oldValue - 10;
+        } else {
+            return targetValue;
+        }
+    }
+
+    private void sendPointer(int action, Point point) {
+        MotionEvent event = MotionEvent.obtain(SystemClock.uptimeMillis(),
+                SystemClock.uptimeMillis(), action, point.x, point.y, 0);
+        getInstrumentation().sendPointerSync(event);
+        event.recycle();
+    }
+
+    /**
+     * Removes all icons from homescreen and hotseat.
+     */
+    public void clearHomescreen() throws Throwable {
+        LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
+                LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
+        LauncherSettings.Settings.call(mTargetContext.getContentResolver(),
+                LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
+        LauncherClings.markFirstRunClingDismissed(mTargetContext);
+        ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mTargetContext);
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                // Reset the loader state
+                LauncherAppState.getInstance().getModel().resetLoadedState(true, true);
+            }
+        });
+    }
+
+    /**
+     * Runs the callback on the UI thread and returns the result.
+     */
+    protected <T> T getOnUiThread(final Callable<T> callback) {
+        final AtomicReference<T> result = new AtomicReference<>(null);
+        try {
+            runTestOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        result.set(callback.call());
+                    } catch (Exception e) { }
+                }
+            });
+        } catch (Throwable t) { }
+        return result.get();
+    }
+
+    /**
+     * Finds a widget provider which can fit on the home screen.
+     * @param hasConfigureScreen if true, a provider with a config screen is returned.
+     */
+    protected LauncherAppWidgetProviderInfo findWidgetProvider(final boolean hasConfigureScreen) {
+        LauncherAppWidgetProviderInfo info = getOnUiThread(new Callable<LauncherAppWidgetProviderInfo>() {
+            @Override
+            public LauncherAppWidgetProviderInfo call() throws Exception {
+                InvariantDeviceProfile idv =
+                        LauncherAppState.getInstance().getInvariantDeviceProfile();
+
+                ComponentName searchComponent = ((SearchManager) mTargetContext
+                        .getSystemService(Context.SEARCH_SERVICE)).getGlobalSearchActivity();
+                String searchPackage = searchComponent == null
+                        ? null : searchComponent.getPackageName();
+
+                for (AppWidgetProviderInfo info :
+                        AppWidgetManagerCompat.getInstance(mTargetContext).getAllProviders()) {
+                    if ((info.configure != null) ^ hasConfigureScreen) {
+                        continue;
+                    }
+                    // Exclude the widgets in search package, as Launcher already binds them in
+                    // QSB, so they can cause conflicts.
+                    if (info.provider.getPackageName().equals(searchPackage)) {
+                        continue;
+                    }
+                    LauncherAppWidgetProviderInfo widgetInfo = LauncherAppWidgetProviderInfo
+                            .fromProviderInfo(mTargetContext, info);
+                    if (widgetInfo.minSpanX >= idv.numColumns
+                            || widgetInfo.minSpanY >= idv.numRows) {
+                        continue;
+                    }
+                    return widgetInfo;
+                }
+                return null;
+            }
+        });
+        if (info == null) {
+            throw new IllegalArgumentException("No valid widget provider");
+        }
+        return info;
+    }
+
+    protected UiObject2 findViewById(int id) {
+        return mDevice.wait(Until.findObject(getSelectorForId(id)), DEFAULT_UI_TIMEOUT);
+    }
+
+    protected BySelector getSelectorForId(int id) {
+        String name = mTargetContext.getResources().getResourceEntryName(id);
+        return By.res(mTargetPackage, name);
+    }
+}
diff --git a/tests/src/com/android/launcher3/RotationPreferenceTest.java b/tests/src/com/android/launcher3/ui/RotationPreferenceTest.java
similarity index 73%
rename from tests/src/com/android/launcher3/RotationPreferenceTest.java
rename to tests/src/com/android/launcher3/ui/RotationPreferenceTest.java
index 0168ee6..e84ad04 100644
--- a/tests/src/com/android/launcher3/RotationPreferenceTest.java
+++ b/tests/src/com/android/launcher3/ui/RotationPreferenceTest.java
@@ -1,22 +1,20 @@
-package com.android.launcher3;
+package com.android.launcher3.ui;
 
-import android.content.Context;
-import android.content.Intent;
 import android.content.SharedPreferences;
 import android.graphics.Rect;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiSelector;
-import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 
 /**
  * Test for auto rotate preference.
  */
-public class RotationPreferenceTest extends InstrumentationTestCase {
-
-    private UiDevice mDevice;
-    private Context mTargetContext;
-    private String mTargetPackage;
+@MediumTest
+public class RotationPreferenceTest extends LauncherInstrumentationTestCase {
 
     private SharedPreferences mPrefs;
     private boolean mOriginalRotationValue;
@@ -46,7 +44,7 @@
 
         setRotationEnabled(false);
         mDevice.setOrientationRight();
-        goToLauncher();
+        startLauncher();
 
         Rect hotseat = getHotseatBounds();
         assertTrue(hotseat.width() > hotseat.height());
@@ -60,21 +58,12 @@
 
         setRotationEnabled(true);
         mDevice.setOrientationRight();
-        goToLauncher();
+        startLauncher();
 
         Rect hotseat = getHotseatBounds();
         assertTrue(hotseat.width() < hotseat.height());
     }
 
-    private void goToLauncher() {
-        Intent homeIntent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .setPackage(mTargetPackage)
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        getInstrumentation().getContext().startActivity(homeIntent);
-        mDevice.findObject(new UiSelector().packageName(mTargetPackage)).waitForExists(6000);
-    }
-
     private void setRotationEnabled(boolean enabled) {
         mPrefs.edit().putBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, enabled).commit();
     }
diff --git a/tests/src/com/android/launcher3/util/Condition.java b/tests/src/com/android/launcher3/util/Condition.java
new file mode 100644
index 0000000..e9ee67c
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/Condition.java
@@ -0,0 +1,54 @@
+package com.android.launcher3.util;
+
+import android.support.test.uiautomator.UiObject2;
+
+import com.android.launcher3.MainThreadExecutor;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public abstract class Condition {
+
+    public abstract boolean isTrue() throws Throwable;
+
+    /**
+     * Converts the condition to be run on UI thread.
+     */
+    public static Condition runOnUiThread(final Condition condition) {
+        final MainThreadExecutor executor = new MainThreadExecutor();
+        return new Condition() {
+            @Override
+            public boolean isTrue() throws Throwable {
+                final AtomicBoolean value = new AtomicBoolean(false);
+                final Throwable[] exceptions = new Throwable[1];
+                final CountDownLatch latch = new CountDownLatch(1);
+                executor.execute(new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            value.set(condition.isTrue());
+                        } catch (Throwable e) {
+                            exceptions[0] = e;
+                        }
+
+                    }
+                });
+                latch.await(1, TimeUnit.SECONDS);
+                if (exceptions[0] != null) {
+                    throw exceptions[0];
+                }
+                return value.get();
+            }
+        };
+    }
+
+    public static Condition minChildCount(final UiObject2 obj, final int childCount) {
+        return new Condition() {
+            @Override
+            public boolean isTrue() {
+                return obj.getChildCount() >= childCount;
+            }
+        };
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/FocusLogicTest.java b/tests/src/com/android/launcher3/util/FocusLogicTest.java
index 386f7dd..eee567f 100644
--- a/tests/src/com/android/launcher3/util/FocusLogicTest.java
+++ b/tests/src/com/android/launcher3/util/FocusLogicTest.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.util;
 
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
diff --git a/tests/src/com/android/launcher3/util/GridOccupancyTest.java b/tests/src/com/android/launcher3/util/GridOccupancyTest.java
new file mode 100644
index 0000000..7d0fe71
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/GridOccupancyTest.java
@@ -0,0 +1,61 @@
+package com.android.launcher3.util;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link GridOccupancy}
+ */
+@SmallTest
+public class GridOccupancyTest extends TestCase {
+
+    public void testFindVacantCell() {
+        GridOccupancy grid = initGrid(4,
+                1, 1, 1, 0, 0,
+                0, 0, 1, 1, 0,
+                0, 0, 0, 0, 0,
+                1, 1, 0, 0, 0
+                );
+
+        int[] vacant = new int[2];
+        assertTrue(grid.findVacantCell(vacant, 2, 2));
+        assertEquals(vacant[0], 0);
+        assertEquals(vacant[1], 1);
+
+        assertTrue(grid.findVacantCell(vacant, 3, 2));
+        assertEquals(vacant[0], 2);
+        assertEquals(vacant[1], 2);
+
+        assertFalse(grid.findVacantCell(vacant, 3, 3));
+    }
+
+    public void testIsRegionVacant() {
+        GridOccupancy grid = initGrid(4,
+                1, 1, 1, 0, 0,
+                0, 0, 1, 1, 0,
+                0, 0, 0, 0, 0,
+                1, 1, 0, 0, 0
+        );
+
+        assertTrue(grid.isRegionVacant(4, 0, 1, 4));
+        assertTrue(grid.isRegionVacant(0, 1, 2, 2));
+        assertTrue(grid.isRegionVacant(2, 2, 3, 2));
+
+        assertFalse(grid.isRegionVacant(3, 0, 2, 4));
+        assertFalse(grid.isRegionVacant(0, 0, 2, 1));
+    }
+
+    private GridOccupancy initGrid(int rows, int... cells) {
+        int cols = cells.length / rows;
+        int i = 0;
+        GridOccupancy grid = new GridOccupancy(cols, rows);
+        for (int y = 0; y < rows; y++) {
+            for (int x = 0; x < cols; x++) {
+                grid.cells[x][y] = cells[i] != 0;
+                i++;
+            }
+        }
+        return grid;
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/TestLauncherProvider.java b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
index 8758f55..6ca2121 100644
--- a/tests/src/com/android/launcher3/util/TestLauncherProvider.java
+++ b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
@@ -1,8 +1,8 @@
 package com.android.launcher3.util;
 
 import android.content.Context;
+import android.database.sqlite.SQLiteOpenHelper;
 
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherProvider;
 
 /**
@@ -22,16 +22,22 @@
         }
     }
 
+    public SQLiteOpenHelper getHelper() {
+        createDbIfNotExists();
+        return mOpenHelper;
+    }
+
     @Override
     protected void notifyListeners() { }
 
     private static class MyDatabaseHelper extends DatabaseHelper {
         public MyDatabaseHelper(Context context) {
-            super(context, null);
+            super(context, null, null);
+            initIds();
         }
 
         @Override
-        protected long getDefaultUserSerial() {
+        public long getDefaultUserSerial() {
             return 0;
         }
 
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
new file mode 100644
index 0000000..f9e53ba
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/Wait.java
@@ -0,0 +1,39 @@
+package com.android.launcher3.util;
+
+import android.os.SystemClock;
+
+/**
+ * A utility class for waiting for a condition to be true.
+ */
+public class Wait {
+
+    private static final long DEFAULT_SLEEP_MS = 200;
+
+    public static boolean atMost(Condition condition, long timeout) {
+        return atMost(condition, timeout, DEFAULT_SLEEP_MS);
+    }
+
+    public static boolean atMost(Condition condition, long timeout, long sleepMillis) {
+        long endTime = SystemClock.uptimeMillis() + timeout;
+        while (SystemClock.uptimeMillis() < endTime) {
+            try {
+                if (condition.isTrue()) {
+                    return true;
+                }
+            } catch (Throwable t) {
+                // Ignore
+            }
+            SystemClock.sleep(sleepMillis);
+        }
+
+        // Check once more before returning false.
+        try {
+            if (condition.isTrue()) {
+                return true;
+            }
+        } catch (Throwable t) {
+            // Ignore
+        }
+        return false;
+    }
+}
diff --git a/update_gallery_files.py b/update_gallery_files.py
deleted file mode 100644
index 738d225..0000000
--- a/update_gallery_files.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# This script is used to pull the most up-to-date files from
-# Gallery into Launcher (we use some code from the Gallery
-# source). The Launcher versions have some small modifications
-# so do this with care, and be sure you are pulling from the
-# latest version of Gallery
-import os
-import sys
-files = """
-src/android/util/Pools.java
-src/com/android/gallery3d/util/IntArray.java
-src/com/android/gallery3d/common/Utils.java
-src/com/android/gallery3d/exif/ByteBufferInputStream.java
-src/com/android/gallery3d/exif/CountedDataInputStream.java
-src/com/android/gallery3d/exif/ExifData.java
-src/com/android/gallery3d/exif/ExifInterface.java
-src/com/android/gallery3d/exif/ExifInvalidFormatException.java
-src/com/android/gallery3d/exif/ExifModifier.java
-src/com/android/gallery3d/exif/ExifOutputStream.java
-src/com/android/gallery3d/exif/ExifParser.java
-src/com/android/gallery3d/exif/ExifReader.java
-src/com/android/gallery3d/exif/ExifTag.java
-src/com/android/gallery3d/exif/IfdData.java
-src/com/android/gallery3d/exif/IfdId.java
-src/com/android/gallery3d/exif/JpegHeader.java
-src/com/android/gallery3d/exif/OrderedDataOutputStream.java
-src/com/android/gallery3d/exif/Rational.java
-src/com/android/gallery3d/glrenderer/BasicTexture.java
-src/com/android/gallery3d/glrenderer/BitmapTexture.java
-src/com/android/gallery3d/glrenderer/GLCanvas.java
-src/com/android/gallery3d/glrenderer/GLES20Canvas.java
-src/com/android/gallery3d/glrenderer/GLES20IdImpl.java
-src/com/android/gallery3d/glrenderer/GLId.java
-src/com/android/gallery3d/glrenderer/GLPaint.java
-src/com/android/gallery3d/glrenderer/RawTexture.java
-src/com/android/gallery3d/glrenderer/Texture.java
-src/com/android/gallery3d/glrenderer/UploadedTexture.java
-src/com/android/photos/BitmapRegionTileSource.java
-src/com/android/photos/views/BlockingGLTextureView.java
-src/com/android/photos/views/TiledImageRenderer.java
-src/com/android/photos/views/TiledImageView.java
-src/com/android/gallery3d/common/BitmapUtils.java
-"""
-
-if len(sys.argv) != 2:
-    print "Usage: python update_gallery_files.py <gallery_dir>"
-    exit()
-gallery_dir = sys.argv[1]
-for file_path in files.split():
-    dir = os.path.dirname(file_path)
-    if file_path.find('exif') != -1 or file_path.find('common') != -1:
-        file_path = 'gallerycommon/' + file_path
-    cmd = 'cp %s/%s WallpaperPicker/%s/' % (gallery_dir, file_path, dir)
-    print cmd
-    os.system(cmd)
diff --git a/update_system_wallpaper_cropper.py b/update_system_wallpaper_cropper.py
deleted file mode 100644
index 44cbcc9..0000000
--- a/update_system_wallpaper_cropper.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# This script is used to push the most up-to-date files from
-# Launcher into frameworks' version of the WallpaperCropActivity
-# (and supporting files)
-# The framework versions have some small modifications that are
-# necessary so do this with care
-import os
-import sys
-src_dir = "WallpaperPicker/src/"
-files = """
-src/android/util/Pools.java
-com/android/gallery3d/util/IntArray.java
-com/android/gallery3d/common/Utils.java
-com/android/gallery3d/exif/ByteBufferInputStream.java
-com/android/gallery3d/exif/CountedDataInputStream.java
-com/android/gallery3d/exif/ExifData.java
-com/android/gallery3d/exif/ExifInterface.java
-com/android/gallery3d/exif/ExifInvalidFormatException.java
-com/android/gallery3d/exif/ExifModifier.java
-com/android/gallery3d/exif/ExifOutputStream.java
-com/android/gallery3d/exif/ExifParser.java
-com/android/gallery3d/exif/ExifReader.java
-com/android/gallery3d/exif/ExifTag.java
-com/android/gallery3d/exif/IfdData.java
-com/android/gallery3d/exif/IfdId.java
-com/android/gallery3d/exif/JpegHeader.java
-com/android/gallery3d/exif/OrderedDataOutputStream.java
-com/android/gallery3d/exif/Rational.java
-com/android/gallery3d/glrenderer/BasicTexture.java
-com/android/gallery3d/glrenderer/BitmapTexture.java
-com/android/gallery3d/glrenderer/GLCanvas.java
-com/android/gallery3d/glrenderer/GLES20Canvas.java
-com/android/gallery3d/glrenderer/GLES20IdImpl.java
-com/android/gallery3d/glrenderer/GLId.java
-com/android/gallery3d/glrenderer/GLPaint.java
-com/android/gallery3d/glrenderer/RawTexture.java
-com/android/gallery3d/glrenderer/Texture.java
-com/android/gallery3d/glrenderer/UploadedTexture.java
-com/android/photos/BitmapRegionTileSource.java
-com/android/photos/views/BlockingGLTextureView.java
-com/android/photos/views/TiledImageRenderer.java
-com/android/photos/views/TiledImageView.java
-com/android/gallery3d/common/BitmapUtils.java
-com/android/launcher3/CropView.java
-com/android/launcher3/WallpaperCropActivity.java
-"""
-
-if len(sys.argv) != 2:
-    print "Usage: python update_sytem_wallpaper_cropper.py <framework_dir>"
-    exit()
-framework_dir = sys.argv[1] + "/packages/WallpaperCropper"
-for file_path in files.split():
-    file_path = src_dir + file_path
-    dir = os.path.dirname(file_path)
-    dir = dir.replace("launcher3", "wallpapercropper")
-    dir = dir.replace(src_dir, "src/")
-    cmd = 'cp %s %s/%s' % (file_path, framework_dir, dir)
-    print cmd
-    os.system(cmd)