On some "non-standard" devices, load label can cause a security exception due to modified platform code
am: 0d86a8dccf  -s ours

* commit '0d86a8dccf6557587eb3abae1bb3573e2c40c12c':
  On some "non-standard" devices, load label can cause a security exception due to modified platform code

Change-Id: Ic2256db82871f26497f699dd4ded276db63ff0ac
diff --git a/Android.mk b/Android.mk
index 1dde46b..dc43f7e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -25,15 +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) \
     $(call all-proto-files-under, protos)
 
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/WallpaperPicker/res \
+LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/res \
-    prebuilts/sdk/current/support/v7/recyclerview/res
+    frameworks/support/v7/recyclerview/res
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 
@@ -41,9 +41,10 @@
 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_OVERRIDES_PACKAGES := Home Launcher2
 
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 918ae52..75b81d0 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,7 +20,7 @@
 <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"/>
 
     <permission
         android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
@@ -40,32 +40,22 @@
         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"
@@ -78,7 +68,7 @@
             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"
@@ -94,37 +84,13 @@
         </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>
-
-        <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 -->
@@ -143,11 +109,10 @@
             </intent-filter>
         </receiver>
 
-        <receiver android:name="com.android.launcher3.StartupReceiver" >
-            <intent-filter>
-                <action android:name="android.intent.action.BOOT_COMPLETED" />
-            </intent-filter>
-        </receiver>
+        <service android:name=".dynamicui.ColorExtractionService"
+            android:exported="false"
+            android:process=":wallpaper_chooser">
+        </service>
 
         <!-- The settings provider contains Home's data, like the workspace favorites -->
         <provider
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/ic_tick.xml b/WallpaperPicker/res/drawable-v21/ic_tick.xml
deleted file mode 100644
index 5b27027..0000000
--- a/WallpaperPicker/res/drawable-v21/ic_tick.xml
+++ /dev/null
@@ -1,31 +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.
--->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:height="48dp"
-    android:viewportHeight="48"
-    android:viewportWidth="48"
-    android:width="48dp" >
-
-    <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>
-
-</vector>
\ No newline at end of file
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 8e349b7..0000000
--- a/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml
+++ /dev/null
@@ -1,32 +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">
-</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 bc87fe1..0000000
--- a/WallpaperPicker/res/values-af/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d gekies"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d gekies"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-am/strings.xml b/WallpaperPicker/res/values-am/strings.xml
deleted file mode 100644
index 23bf538..0000000
--- a/WallpaperPicker/res/values-am/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d ተመርጧል"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d ተመርጧል"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-ar/strings.xml b/WallpaperPicker/res/values-ar/strings.xml
deleted file mode 100644
index db6834c..0000000
--- a/WallpaperPicker/res/values-ar/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"‏تم تحديد %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"‏تم تحديد %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"‏تم تحديد %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>
-</resources>
diff --git a/WallpaperPicker/res/values-az-rAZ/strings.xml b/WallpaperPicker/res/values-az-rAZ/strings.xml
deleted file mode 100644
index 883673d..0000000
--- a/WallpaperPicker/res/values-az-rAZ/strings.xml
+++ /dev/null
@@ -1,36 +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>
-  <plurals name="number_of_items_selected">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d seçilib"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d seçilib"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-bg/strings.xml b/WallpaperPicker/res/values-bg/strings.xml
deleted file mode 100644
index bf2a83b..0000000
--- a/WallpaperPicker/res/values-bg/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Избрахте %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Избрахте %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"Избрахте %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>
-</resources>
diff --git a/WallpaperPicker/res/values-bn-rBD/strings.xml b/WallpaperPicker/res/values-bn-rBD/strings.xml
deleted file mode 100644
index 1c4d3d4..0000000
--- a/WallpaperPicker/res/values-bn-rBD/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$dটি নির্বাচন করা হয়েছে"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$dটি নির্বাচন করা হয়েছে"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-ca/strings.xml b/WallpaperPicker/res/values-ca/strings.xml
deleted file mode 100644
index 9533ae9..0000000
--- a/WallpaperPicker/res/values-ca/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Seleccionats: %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Seleccionats: %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"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>
-</resources>
diff --git a/WallpaperPicker/res/values-cs/strings.xml b/WallpaperPicker/res/values-cs/strings.xml
deleted file mode 100644
index aab8cc8..0000000
--- a/WallpaperPicker/res/values-cs/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Vybráno: %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Vybráno: %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"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>
-</resources>
diff --git a/WallpaperPicker/res/values-da/strings.xml b/WallpaperPicker/res/values-da/strings.xml
deleted file mode 100644
index 10ef5b3..0000000
--- a/WallpaperPicker/res/values-da/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d er valgt"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d er valgt"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-de/strings.xml b/WallpaperPicker/res/values-de/strings.xml
deleted file mode 100644
index be35b73..0000000
--- a/WallpaperPicker/res/values-de/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d ausgewählt"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d ausgewählt"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-el/strings.xml b/WallpaperPicker/res/values-el/strings.xml
deleted file mode 100644
index 1d799a8..0000000
--- a/WallpaperPicker/res/values-el/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d επιλεγμένα"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d επιλεγμένα"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-en-rAU/strings.xml b/WallpaperPicker/res/values-en-rAU/strings.xml
deleted file mode 100644
index a384ff6..0000000
--- a/WallpaperPicker/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d selected"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d selected"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-en-rGB/strings.xml b/WallpaperPicker/res/values-en-rGB/strings.xml
deleted file mode 100644
index a384ff6..0000000
--- a/WallpaperPicker/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d selected"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d selected"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-en-rIN/strings.xml b/WallpaperPicker/res/values-en-rIN/strings.xml
deleted file mode 100644
index a384ff6..0000000
--- a/WallpaperPicker/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d selected"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d selected"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-es-rUS/strings.xml b/WallpaperPicker/res/values-es-rUS/strings.xml
deleted file mode 100644
index 924ea56..0000000
--- a/WallpaperPicker/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d seleccionado"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d seleccionado"</item>
-    <item quantity="other" msgid="479468347731745357">"%1$d seleccionados"</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>
-</resources>
diff --git a/WallpaperPicker/res/values-es/strings.xml b/WallpaperPicker/res/values-es/strings.xml
deleted file mode 100644
index 8ecd3f4..0000000
--- a/WallpaperPicker/res/values-es/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Seleccionados: %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Seleccionados: %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"Seleccionados: %1$d"</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>
-</resources>
diff --git a/WallpaperPicker/res/values-et-rEE/strings.xml b/WallpaperPicker/res/values-et-rEE/strings.xml
deleted file mode 100644
index 59ca770..0000000
--- a/WallpaperPicker/res/values-et-rEE/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Valitud on %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Valitud on %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"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>
-</resources>
diff --git a/WallpaperPicker/res/values-eu-rES/strings.xml b/WallpaperPicker/res/values-eu-rES/strings.xml
deleted file mode 100644
index fb77b1a..0000000
--- a/WallpaperPicker/res/values-eu-rES/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d hautatuta"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d hautatuta"</item>
-    <item quantity="other" msgid="479468347731745357">"%1$d hautatuta"</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>
-</resources>
diff --git a/WallpaperPicker/res/values-fa/strings.xml b/WallpaperPicker/res/values-fa/strings.xml
deleted file mode 100644
index da4b7a1..0000000
--- a/WallpaperPicker/res/values-fa/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"‏%1$d انتخاب شد"</item>
-    <item quantity="one" msgid="8409622005831789373">"‏%1$d انتخاب شد"</item>
-    <item quantity="other" msgid="479468347731745357">"‏%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>
-</resources>
diff --git a/WallpaperPicker/res/values-fi/strings.xml b/WallpaperPicker/res/values-fi/strings.xml
deleted file mode 100644
index 3c8f1f5..0000000
--- a/WallpaperPicker/res/values-fi/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d valittu"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d valittu"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-fr-rCA/strings.xml b/WallpaperPicker/res/values-fr-rCA/strings.xml
deleted file mode 100644
index ba1d430..0000000
--- a/WallpaperPicker/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d sélectionné"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d sélectionné"</item>
-    <item quantity="other" msgid="479468347731745357">"%1$d 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>
-</resources>
diff --git a/WallpaperPicker/res/values-fr/strings.xml b/WallpaperPicker/res/values-fr/strings.xml
deleted file mode 100644
index 9f3b525..0000000
--- a/WallpaperPicker/res/values-fr/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d élément sélectionné"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d élément sélectionné"</item>
-    <item quantity="other" msgid="479468347731745357">"%1$d éléments 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>
-</resources>
diff --git a/WallpaperPicker/res/values-gl-rES/strings.xml b/WallpaperPicker/res/values-gl-rES/strings.xml
deleted file mode 100644
index 5805489..0000000
--- a/WallpaperPicker/res/values-gl-rES/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Seleccionaches %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Seleccionaches %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"Seleccionaches %1$d"</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>
-</resources>
diff --git a/WallpaperPicker/res/values-gu-rIN/strings.xml b/WallpaperPicker/res/values-gu-rIN/strings.xml
deleted file mode 100644
index e201d52..0000000
--- a/WallpaperPicker/res/values-gu-rIN/strings.xml
+++ /dev/null
@@ -1,36 +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>
-  <plurals name="number_of_items_selected">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d પસંદ કર્યો"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d પસંદ કર્યો"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-hi/strings.xml b/WallpaperPicker/res/values-hi/strings.xml
deleted file mode 100644
index 6f610b5..0000000
--- a/WallpaperPicker/res/values-hi/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d चयनित"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d चयनित"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-hr/strings.xml b/WallpaperPicker/res/values-hr/strings.xml
deleted file mode 100644
index aaf5e1b..0000000
--- a/WallpaperPicker/res/values-hr/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Odabrano je %1$d stavki"</item>
-    <item quantity="one" msgid="8409622005831789373">"Odabrana je %1$d stavka"</item>
-    <item quantity="other" msgid="479468347731745357">"Odabrano stavki: %1$d"</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>
-</resources>
diff --git a/WallpaperPicker/res/values-hu/strings.xml b/WallpaperPicker/res/values-hu/strings.xml
deleted file mode 100644
index 06c0952..0000000
--- a/WallpaperPicker/res/values-hu/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d kiválasztva"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d kiválasztva"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-hy-rAM/strings.xml b/WallpaperPicker/res/values-hy-rAM/strings.xml
deleted file mode 100644
index f68d49f..0000000
--- a/WallpaperPicker/res/values-hy-rAM/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d ընտրված"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d ընտրված"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-in/strings.xml b/WallpaperPicker/res/values-in/strings.xml
deleted file mode 100644
index 634eb1f..0000000
--- a/WallpaperPicker/res/values-in/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d dipilih"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d dipilih"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-is-rIS/strings.xml b/WallpaperPicker/res/values-is-rIS/strings.xml
deleted file mode 100644
index eac44ec..0000000
--- a/WallpaperPicker/res/values-is-rIS/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d valin"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d valið"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-it/strings.xml b/WallpaperPicker/res/values-it/strings.xml
deleted file mode 100644
index fdb0ce8..0000000
--- a/WallpaperPicker/res/values-it/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d selezionati"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d selezionato"</item>
-    <item quantity="other" msgid="479468347731745357">"%1$d selezionati"</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>
-</resources>
diff --git a/WallpaperPicker/res/values-iw/strings.xml b/WallpaperPicker/res/values-iw/strings.xml
deleted file mode 100644
index c6a583d..0000000
--- a/WallpaperPicker/res/values-iw/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"‏%1$d נבחרו"</item>
-    <item quantity="one" msgid="8409622005831789373">"‏%1$d נבחרו"</item>
-    <item quantity="other" msgid="479468347731745357">"‏%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>
-</resources>
diff --git a/WallpaperPicker/res/values-ja/strings.xml b/WallpaperPicker/res/values-ja/strings.xml
deleted file mode 100644
index f18da5d..0000000
--- a/WallpaperPicker/res/values-ja/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d個選択済み"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d個選択済み"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-ka-rGE/strings.xml b/WallpaperPicker/res/values-ka-rGE/strings.xml
deleted file mode 100644
index 1f65282..0000000
--- a/WallpaperPicker/res/values-ka-rGE/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"არჩეულია %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"არჩეულია %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"არჩეულია %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>
-</resources>
diff --git a/WallpaperPicker/res/values-kk-rKZ/strings.xml b/WallpaperPicker/res/values-kk-rKZ/strings.xml
deleted file mode 100644
index 6f4ee7c..0000000
--- a/WallpaperPicker/res/values-kk-rKZ/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d таңдалған"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d таңдалған"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-km-rKH/strings.xml b/WallpaperPicker/res/values-km-rKH/strings.xml
deleted file mode 100644
index 801260d..0000000
--- a/WallpaperPicker/res/values-km-rKH/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"បាន​ជ្រើស %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"បាន​ជ្រើស %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"បាន​ជ្រើស %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>
-</resources>
diff --git a/WallpaperPicker/res/values-kn-rIN/strings.xml b/WallpaperPicker/res/values-kn-rIN/strings.xml
deleted file mode 100644
index 7d4d7e7..0000000
--- a/WallpaperPicker/res/values-kn-rIN/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-ko/strings.xml b/WallpaperPicker/res/values-ko/strings.xml
deleted file mode 100644
index f407294..0000000
--- a/WallpaperPicker/res/values-ko/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d개 선택됨"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d개 선택됨"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-ky-rKG/strings.xml b/WallpaperPicker/res/values-ky-rKG/strings.xml
deleted file mode 100644
index f53f52b..0000000
--- a/WallpaperPicker/res/values-ky-rKG/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d тандалды"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d тандалды"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-lo-rLA/strings.xml b/WallpaperPicker/res/values-lo-rLA/strings.xml
deleted file mode 100644
index 1da6d29..0000000
--- a/WallpaperPicker/res/values-lo-rLA/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"ເລືອກ %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"ເລືອກ %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"ເລືອກ %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>
-</resources>
diff --git a/WallpaperPicker/res/values-lt/strings.xml b/WallpaperPicker/res/values-lt/strings.xml
deleted file mode 100644
index 98c9a36..0000000
--- a/WallpaperPicker/res/values-lt/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Pasirinkta: %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Pasirinkta: %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"Pasirinkta: %1$d"</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>
-</resources>
diff --git a/WallpaperPicker/res/values-lv/strings.xml b/WallpaperPicker/res/values-lv/strings.xml
deleted file mode 100644
index ff7876c..0000000
--- a/WallpaperPicker/res/values-lv/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Atlasīts: %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Atlasīta: %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"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>
-</resources>
diff --git a/WallpaperPicker/res/values-mk-rMK/strings.xml b/WallpaperPicker/res/values-mk-rMK/strings.xml
deleted file mode 100644
index 13b38cd..0000000
--- a/WallpaperPicker/res/values-mk-rMK/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Избрано %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Избрано %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"Избрано %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>
-</resources>
diff --git a/WallpaperPicker/res/values-ml-rIN/strings.xml b/WallpaperPicker/res/values-ml-rIN/strings.xml
deleted file mode 100644
index 5831b36..0000000
--- a/WallpaperPicker/res/values-ml-rIN/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d തിരഞ്ഞെടുത്തു"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d തിരഞ്ഞെടുത്തു"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-mn-rMN/strings.xml b/WallpaperPicker/res/values-mn-rMN/strings.xml
deleted file mode 100644
index 9995547..0000000
--- a/WallpaperPicker/res/values-mn-rMN/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d сонгогдсон"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d сонгогдсон"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-mr-rIN/strings.xml b/WallpaperPicker/res/values-mr-rIN/strings.xml
deleted file mode 100644
index d740fd2..0000000
--- a/WallpaperPicker/res/values-mr-rIN/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d निवडले"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d निवडले"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-ms-rMY/strings.xml b/WallpaperPicker/res/values-ms-rMY/strings.xml
deleted file mode 100644
index 759e48c..0000000
--- a/WallpaperPicker/res/values-ms-rMY/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d dipilih"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d dipilih"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-my-rMM/strings.xml b/WallpaperPicker/res/values-my-rMM/strings.xml
deleted file mode 100644
index 5197b98..0000000
--- a/WallpaperPicker/res/values-my-rMM/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d ရွေးချယ်ပြီး"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d ရွေးချယ်ပြီး"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-nb/strings.xml b/WallpaperPicker/res/values-nb/strings.xml
deleted file mode 100644
index 8125b53..0000000
--- a/WallpaperPicker/res/values-nb/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d valgt"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d valgt"</item>
-    <item quantity="other" msgid="479468347731745357">"%1$d 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>
-</resources>
diff --git a/WallpaperPicker/res/values-ne-rNP/strings.xml b/WallpaperPicker/res/values-ne-rNP/strings.xml
deleted file mode 100644
index b77a1c5..0000000
--- a/WallpaperPicker/res/values-ne-rNP/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d चयन भयो"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d चयन भयो"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-nl/strings.xml b/WallpaperPicker/res/values-nl/strings.xml
deleted file mode 100644
index dc78305..0000000
--- a/WallpaperPicker/res/values-nl/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d geselecteerd"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d geselecteerd"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</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 e4225e0..0000000
--- a/WallpaperPicker/res/values-pa-rIN/strings.xml
+++ /dev/null
@@ -1,36 +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>
-  <plurals name="number_of_items_selected">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d ਚੁਣਿਆ ਗਿਆ"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d ਚੁਣਿਆ ਗਿਆ"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-pl/strings.xml b/WallpaperPicker/res/values-pl/strings.xml
deleted file mode 100644
index 9693de4..0000000
--- a/WallpaperPicker/res/values-pl/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Wybranych %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Wybrana %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"Wybrane: %1$d"</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>
-</resources>
diff --git a/WallpaperPicker/res/values-pt-rPT/strings.xml b/WallpaperPicker/res/values-pt-rPT/strings.xml
deleted file mode 100644
index 3c4fa9b..0000000
--- a/WallpaperPicker/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d selecionadas"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d selecionada"</item>
-    <item quantity="other" msgid="479468347731745357">"%1$d selecionadas"</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>
-</resources>
diff --git a/WallpaperPicker/res/values-pt/strings.xml b/WallpaperPicker/res/values-pt/strings.xml
deleted file mode 100644
index 2520eed..0000000
--- a/WallpaperPicker/res/values-pt/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d selecionados"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d selecionado"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-ro/strings.xml b/WallpaperPicker/res/values-ro/strings.xml
deleted file mode 100644
index f5df3ee..0000000
--- a/WallpaperPicker/res/values-ro/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d selectate"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d selectată"</item>
-    <item quantity="other" msgid="479468347731745357">"%1$d selectate"</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>
-</resources>
diff --git a/WallpaperPicker/res/values-ru/strings.xml b/WallpaperPicker/res/values-ru/strings.xml
deleted file mode 100644
index f8c350a..0000000
--- a/WallpaperPicker/res/values-ru/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Выбрано: %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Выбрано: %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"Выбрано: %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>
-</resources>
diff --git a/WallpaperPicker/res/values-si-rLK/strings.xml b/WallpaperPicker/res/values-si-rLK/strings.xml
deleted file mode 100644
index 3945bdf..0000000
--- a/WallpaperPicker/res/values-si-rLK/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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d තෝරා ගන්නා ලදි"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d තෝරා ගන්නා ලදි"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-sk/strings.xml b/WallpaperPicker/res/values-sk/strings.xml
deleted file mode 100644
index fb2c819..0000000
--- a/WallpaperPicker/res/values-sk/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Počet vybratých položiek: %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Počet vybratých položiek: %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"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>
-</resources>
diff --git a/WallpaperPicker/res/values-sl/strings.xml b/WallpaperPicker/res/values-sl/strings.xml
deleted file mode 100644
index a7ff089..0000000
--- a/WallpaperPicker/res/values-sl/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Št. izbranih: %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Št. izbranih: %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"Št. izbranih: %1$d"</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>
-</resources>
diff --git a/WallpaperPicker/res/values-sq-rAL/strings.xml b/WallpaperPicker/res/values-sq-rAL/strings.xml
deleted file mode 100644
index 8a9983b..0000000
--- a/WallpaperPicker/res/values-sq-rAL/strings.xml
+++ /dev/null
@@ -1,36 +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>
-  <plurals name="number_of_items_selected">
-    <item quantity="zero" msgid="9015111147509924344">"Të përzgjedhur: %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Të përzgjedhur: %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"Të përzgjedhur: %1$d"</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>
-</resources>
diff --git a/WallpaperPicker/res/values-sr/strings.xml b/WallpaperPicker/res/values-sr/strings.xml
deleted file mode 100644
index 6154526..0000000
--- a/WallpaperPicker/res/values-sr/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Изабрано је %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Изабрана је %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"Изабраних: %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>
-</resources>
diff --git a/WallpaperPicker/res/values-sv/strings.xml b/WallpaperPicker/res/values-sv/strings.xml
deleted file mode 100644
index 38062b9..0000000
--- a/WallpaperPicker/res/values-sv/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d har valts"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d har valts"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-sw/strings.xml b/WallpaperPicker/res/values-sw/strings.xml
deleted file mode 100644
index 729a79a..0000000
--- a/WallpaperPicker/res/values-sw/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d zimechaguliwa"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d zimechaguliwa"</item>
-    <item quantity="other" msgid="479468347731745357">"%1$d zimechaguliwa"</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>
-</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 69a9d38..0000000
--- a/WallpaperPicker/res/values-ta-rIN/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d தேர்ந்தெடுக்கப்பட்டன"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d தேர்ந்தெடுக்கப்பட்டது"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-te-rIN/strings.xml b/WallpaperPicker/res/values-te-rIN/strings.xml
deleted file mode 100644
index 6fb5fa2..0000000
--- a/WallpaperPicker/res/values-te-rIN/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d ఎంచుకోబడింది"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d ఎంచుకోబడింది"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-th/strings.xml b/WallpaperPicker/res/values-th/strings.xml
deleted file mode 100644
index c689436..0000000
--- a/WallpaperPicker/res/values-th/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"เลือกไว้ %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"เลือกไว้ %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"เลือกไว้ %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>
-</resources>
diff --git a/WallpaperPicker/res/values-tl/strings.xml b/WallpaperPicker/res/values-tl/strings.xml
deleted file mode 100644
index c760d7f..0000000
--- a/WallpaperPicker/res/values-tl/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d ang napili"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d ang napili"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-tr/strings.xml b/WallpaperPicker/res/values-tr/strings.xml
deleted file mode 100644
index e9dc1d2..0000000
--- a/WallpaperPicker/res/values-tr/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d tane seçildi"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d tane seçildi"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</resources>
diff --git a/WallpaperPicker/res/values-uk/strings.xml b/WallpaperPicker/res/values-uk/strings.xml
deleted file mode 100644
index c6669c0..0000000
--- a/WallpaperPicker/res/values-uk/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Вибрано %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Вибрано %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"Вибрано %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>
-</resources>
diff --git a/WallpaperPicker/res/values-ur-rPK/strings.xml b/WallpaperPicker/res/values-ur-rPK/strings.xml
deleted file mode 100644
index e240e1a..0000000
--- a/WallpaperPicker/res/values-ur-rPK/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"‏%1$d کو منتخب کیا گیا"</item>
-    <item quantity="one" msgid="8409622005831789373">"‏%1$d کو منتخب کیا گیا"</item>
-    <item quantity="other" msgid="479468347731745357">"‏%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>
-</resources>
diff --git a/WallpaperPicker/res/values-uz-rUZ/strings.xml b/WallpaperPicker/res/values-uz-rUZ/strings.xml
deleted file mode 100644
index 5a79981..0000000
--- a/WallpaperPicker/res/values-uz-rUZ/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d ta tanlandi"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d ta tanlandi"</item>
-    <item quantity="other" msgid="479468347731745357">"%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>
-</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 a7c636d..0000000
--- a/WallpaperPicker/res/values-vi/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"Đã chọn %1$d"</item>
-    <item quantity="one" msgid="8409622005831789373">"Đã chọn %1$d"</item>
-    <item quantity="other" msgid="479468347731745357">"Đã chọn %1$d"</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>
-</resources>
diff --git a/WallpaperPicker/res/values-zh-rCN/strings.xml b/WallpaperPicker/res/values-zh-rCN/strings.xml
deleted file mode 100644
index 4656ec6..0000000
--- a/WallpaperPicker/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"已选择%1$d项"</item>
-    <item quantity="one" msgid="8409622005831789373">"已选择%1$d项"</item>
-    <item quantity="other" msgid="479468347731745357">"已选择%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>
-</resources>
diff --git a/WallpaperPicker/res/values-zh-rHK/strings.xml b/WallpaperPicker/res/values-zh-rHK/strings.xml
deleted file mode 100644
index eb9c327..0000000
--- a/WallpaperPicker/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"已選取 %1$d 張"</item>
-    <item quantity="one" msgid="8409622005831789373">"已選取 %1$d 張"</item>
-    <item quantity="other" msgid="479468347731745357">"已選取 %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>
-</resources>
diff --git a/WallpaperPicker/res/values-zh-rTW/strings.xml b/WallpaperPicker/res/values-zh-rTW/strings.xml
deleted file mode 100644
index fda123c..0000000
--- a/WallpaperPicker/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"已選取 %1$d 個"</item>
-    <item quantity="one" msgid="8409622005831789373">"已選取 %1$d 個"</item>
-    <item quantity="other" msgid="479468347731745357">"已選取 %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>
-</resources>
diff --git a/WallpaperPicker/res/values-zu/strings.xml b/WallpaperPicker/res/values-zu/strings.xml
deleted file mode 100644
index 1a5b95e..0000000
--- a/WallpaperPicker/res/values-zu/strings.xml
+++ /dev/null
@@ -1,37 +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">
-    <item quantity="zero" msgid="9015111147509924344">"%1$d khethiwe"</item>
-    <item quantity="one" msgid="8409622005831789373">"%1$d khethiwe"</item>
-    <item quantity="other" msgid="479468347731745357">"%1$d khethiwe"</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>
-</resources>
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 2bfd476..0000000
--- a/WallpaperPicker/res/values/strings.xml
+++ /dev/null
@@ -1,56 +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>
-</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 8b51447..0000000
--- a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
+++ /dev/null
@@ -1,409 +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.R;
-
-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<Void, Void, Boolean> {
-
-    public interface OnBitmapCroppedHandler {
-        public void onBitmapCropped(byte[] imageBytes);
-    }
-
-    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(Context c, String filePath,
-            RectF cropBounds, int rotation, int outWidth, int outHeight,
-            boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) {
-        mContext = c;
-        mInFilePath = filePath;
-        init(cropBounds, rotation,
-                outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndCropHandler);
-    }
-
-    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() {
-        boolean failure = false;
-
-
-        WallpaperManager wallpaperManager = null;
-        if (mSetWallpaper) {
-            wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
-        }
-
-
-        if (mSetWallpaper && mNoCrop) {
-            try {
-                InputStream is = regenerateInputStream();
-                if (is != null) {
-                    wallpaperManager.setStream(is);
-                    Utils.closeSilently(is);
-                }
-            } catch (IOException e) {
-                Log.w(LOGTAG, "cannot write stream to wallpaper", e);
-                failure = true;
-            }
-            return !failure;
-        } 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 && wallpaperManager != null) {
-                    try {
-                        byte[] outByteArray = tmpOut.toByteArray();
-                        wallpaperManager.setStream(new ByteArrayInputStream(outByteArray));
-                        if (mOnBitmapCroppedHandler != null) {
-                            mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
-                        }
-                    } 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(Void... params) {
-        return cropBitmap();
-    }
-
-    @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);
-        }
-    }
-}
\ 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 e98e23e..0000000
--- a/WallpaperPicker/src/com/android/launcher3/CropView.java
+++ /dev/null
@@ -1,332 +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.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 setScale(float scale) {
-        synchronized (mLock) {
-            mRenderer.scale = 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) {
-    }
-
-    /**
-     * Offsets wallpaper preview according to the state it will be displayed in upon returning home.
-     * @param offset Ranges from 0 to 1, where 0 is the leftmost parallax and 1 is the rightmost.
-     */
-    public void setParallaxOffset(float offset, RectF crop) {
-        offset = Math.max(0, Math.min(offset, 1)); // Make sure the offset is in the correct range.
-        float screenWidth = getWidth() / mRenderer.scale;
-        mCenterX = screenWidth / 2 + offset * (crop.width() - screenWidth) + crop.left;
-        updateCenter();
-    }
-
-    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 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/SavedWallpaperImages.java b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
deleted file mode 100644
index 64b0ac4..0000000
--- a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
+++ /dev/null
@@ -1,224 +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.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 java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-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;
-        public SavedWallpaperTile(int dbId, File target, Drawable thumb) {
-            super(target, thumb);
-            mDbId = dbId;
-        }
-
-        @Override
-        public void onDelete(WallpaperPickerActivity a) {
-            a.getSavedImages().deleteImage(mDbId);
-        }
-    }
-
-    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}, // 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) {
-                mImages.add(new SavedWallpaperTile(result.getInt(0),
-                        new File(mContext.getFilesDir(), result.getString(2)),
-                        new BitmapDrawable(thumb)));
-            }
-        }
-        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 {
-            File imageFile = File.createTempFile("wallpaper", "", mContext.getFilesDir());
-            FileOutputStream imageFileStream =
-                    mContext.openFileOutput(imageFile.getName(), Context.MODE_PRIVATE);
-            imageFileStream.write(imageBytes);
-            imageFileStream.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());
-            db.insert(ImageDb.TABLE_NAME, null, values);
-        } catch (IOException e) {
-            Log.e(TAG, "Failed writing images to storage " + e);
-        }
-    }
-
-    static class ImageDb extends SQLiteOpenHelper {
-        final static int DB_VERSION = 1;
-        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";
-
-        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, " +
-                    "PRIMARY KEY (" + COLUMN_ID + " ASC) " +
-                    ");");
-        }
-
-        @Override
-        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            if (oldVersion != newVersion) {
-                // Delete all the records; they'll be repopulated as this is a cache
-                db.execSQL("DELETE FROM " + TABLE_NAME);
-            }
-        }
-    }
-}
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 75bdb8a..0000000
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
+++ /dev/null
@@ -1,493 +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.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.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>());
-
-    @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);
-                }
-            }
-        }
-    }
-
-    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) {
-                TileSource src = req.result;
-                Point wallpaperSize = WallpaperUtils.getDefaultWallpaperSize(
-                        getResources(), getWindowManager());
-                RectF crop = Utils.getMaxCropRect(src.getImageWidth(), src.getImageHeight(),
-                        wallpaperSize.x, wallpaperSize.y, false /* leftAligned */);
-                mCropView.setScale(req.scaleAndOffsetProvider.getScale(wallpaperSize, crop));
-                mCropView.setParallaxOffset(req.scaleAndOffsetProvider.getParallaxOffset(), crop);
-            }
-
-            // 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);
-        cropTask.execute();
-    }
-
-    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);
-        cropTask.execute();
-    }
-
-    @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);
-        }
-        cropTask.execute();
-    }
-
-    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;
-    }
-
-    interface CropViewScaleAndOffsetProvider {
-        float getScale(Point wallpaperSize, RectF crop);
-        float getParallaxOffset();
-    }
-}
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
deleted file mode 100644
index 5d41238..0000000
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ /dev/null
@@ -1,1205 +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.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.PorterDuff;
-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.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.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-
-public class WallpaperPickerActivity extends WallpaperCropActivity {
-    static final String TAG = "Launcher.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() {
-                public void onBitmapCropped(byte[] imageBytes) {
-                    Point thumbSize = getDefaultThumbnailSize(a.getResources());
-                    // rotation is set to 0 since imageBytes has already been correctly rotated
-                    Bitmap thumb = createThumbnail(
-                            thumbSize, null, null, imageBytes, null, 0, 0, true);
-                    a.getSavedImages().writeImage(thumb, imageBytes);
-                }
-            };
-            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 {
-        private 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, null, new Runnable() {
-
-                @Override
-                public void run() {
-                    if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
-                        a.setWallpaperButtonEnabled(true);
-                    }
-                }
-            });
-        }
-        @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() {
-
-                @Override
-                public float getScale(Point wallpaperSize, RectF crop) {
-                    return 1f;
-                }
-
-                @Override
-                public float getParallaxOffset() {
-                    return 0.5f;
-                }
-            };
-            req.result = new DrawableTileSource(a.getContext(),
-                    defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE);
-            a.onLoadRequestComplete(req, true);
-        }
-        @Override
-        public void onSave(WallpaperPickerActivity a) {
-            try {
-                WallpaperManager.getInstance(a.getContext()).clear();
-                a.setResult(Activity.RESULT_OK);
-            } catch (IOException e) {
-                Log.w("Setting wallpaper to default threw exception", e);
-            }
-            a.finish();
-        }
-        @Override
-        public boolean isSelectable() {
-            return true;
-        }
-        @Override
-        public boolean isNamelessWallpaper() {
-            return true;
-        }
-    }
-
-    /**
-     * 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;
-                }
-                setWallpaperButtonEnabled(true);
-                WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
-                if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
-                    selectTile(v);
-                }
-                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 slelected 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 {
-                            // no tile was selected, so we just finish the activity and go back
-                            setResult(Activity.RESULT_OK);
-                            finish();
-                        }
-                    }
-                });
-        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()) {
-            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() {
-        // Check if rotation is enabled for this device.
-        if (Utilities.isRotationAllowedForDevice(getContext()))
-            return true;
-
-        // Check if the user has specifically enabled rotation via preferences.
-        return Utilities.isAllowRotationPrefEnabled(getApplicationContext(), 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 44f5b7c..2efbec4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,7 +3,7 @@
         mavenCentral()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:+'
+        classpath 'com.android.tools.build:gradle:2.1.0'
         classpath 'com.google.protobuf:protobuf-gradle-plugin:0.7.0'
     }
 }
@@ -17,7 +17,7 @@
 
     defaultConfig {
         applicationId "com.android.launcher3"
-        minSdkVersion 16
+        minSdkVersion 21
         targetSdkVersion 23
         versionCode 1
         versionName "1.0"
@@ -32,15 +32,14 @@
     }
     sourceSets {
         main {
-            res.srcDirs = ['res', 'WallpaperPicker/res']
-            java.srcDirs = ['src', 'WallpaperPicker/src']
+            res.srcDirs = ['res']
+            java.srcDirs = ['src']
             manifest.srcFile 'AndroidManifest.xml'
             proto.srcDirs 'protos/'
         }
 
         androidTest {
             java.srcDirs = ['tests/src']
-            res.srcDirs = ['tests/res']
             manifest.srcFile "tests/AndroidManifest.xml"
         }
     }
@@ -51,13 +50,15 @@
 }
 
 dependencies {
-    compile 'com.android.support:support-v4:23.0.1'
-    compile 'com.android.support:recyclerview-v7:23.0.1'
+    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:+'
-    androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:+'
+    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 {
diff --git a/proguard.flags b/proguard.flags
index 7725800..19d2f0c 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -39,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);
@@ -70,3 +70,10 @@
   public float getBackgroundAlpha();
   public void setBackgroundAlpha(float);
 }
+
+# Proguard will strip new callbacks in LauncherApps.Callback from
+# WrappedCallback if compiled against an older SDK. Don't let this happen.
+-keep class com.android.launcher3.compat.** {
+  *;
+}
+
diff --git a/protos/backup.proto b/protos/backup.proto
index c3f27e1..62f935c 100644
--- a/protos/backup.proto
+++ b/protos/backup.proto
@@ -97,7 +97,7 @@
   optional string appWidgetProvider = 12;
   optional string intent = 13;
   optional string uri = 14;
-  optional int32 iconType = 15;
+  optional int32 iconType = 15 [deprecated = true];
   optional string iconPackage = 16;
   optional string iconResource = 17;
   optional bytes icon = 18;
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
new file mode 100644
index 0000000..eae02ca
--- /dev/null
+++ b/protos/launcher_log.proto
@@ -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.
+ */
+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;
+}
+
+// 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;
+}
+
+// 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;
+  }
+  optional Type type = 1;
+  optional Touch touch = 2;
+}
+
+//
+// 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;
+}
\ No newline at end of file
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/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_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_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-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_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_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-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_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_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-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_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_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-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/all_apps_search_bg.xml b/res/drawable/all_apps_search_bg.xml
index 5a2c9e8..b0ed9b5 100644
--- a/res/drawable/all_apps_search_bg.xml
+++ b/res/drawable/all_apps_search_bg.xml
@@ -30,6 +30,7 @@
         android:bottom="0dp">
 
         <shape android:shape="rectangle">
+            <solid android:color="@android:color/transparent" />
             <stroke
                 android:width="@dimen/all_apps_search_bar_divider_width"
                 android:color="#1E000000"/>
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_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/ic_setting_pressed.xml b/res/drawable/ic_setting_pressed.xml
new file mode 100644
index 0000000..689f833
--- /dev/null
+++ b/res/drawable/ic_setting_pressed.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:alpha="0.5"
+    android:src="@drawable/ic_setting" />
diff --git a/res/drawable/ic_wallpaper_pressed.xml b/res/drawable/ic_wallpaper_pressed.xml
new file mode 100644
index 0000000..d241c7d
--- /dev/null
+++ b/res/drawable/ic_wallpaper_pressed.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:alpha="0.5"
+    android:src="@drawable/ic_wallpaper" />
diff --git a/res/drawable/ic_widget_pressed.xml b/res/drawable/ic_widget_pressed.xml
new file mode 100644
index 0000000..44ac5b6
--- /dev/null
+++ b/res/drawable/ic_widget_pressed.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:alpha="0.5"
+    android:src="@drawable/ic_widget" />
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 1a951f1..3a361e2 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -23,7 +23,7 @@
     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"
@@ -41,8 +41,7 @@
             android:id="@+id/workspace"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:layout_gravity="center"
-            launcher:defaultScreen="@integer/config_workspaceDefaultScreen" />
+            android:layout_gravity="center" />
 
         <!-- DO NOT CHANGE THE ID -->
         <include layout="@layout/hotseat"
@@ -52,8 +51,12 @@
             android:layout_gravity="right" />
 
         <include
+            android:id="@+id/app_info_drop_target_bar"
+            layout="@layout/drop_target_bar_vert_info" />
+
+        <include
             android:id="@+id/search_drop_target_bar"
-            layout="@layout/search_drop_target_bar" />
+            layout="@layout/drop_target_bar_vert_search" />
 
         <include layout="@layout/overview_panel"
             android:id="@+id/overview_panel"
@@ -70,12 +73,6 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:visibility="invisible" />
-    </com.android.launcher3.DragLayer>
+    </com.android.launcher3.dragndrop.DragLayer>
 
-    <ViewStub
-        android:id="@+id/launcher_overlay_stub"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:inflatedId="@+id/launcher_overlay"
-        android:layout="@layout/launcher_overlay" />
 </com.android.launcher3.LauncherRootView>
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 8bf9d64..f711274 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -24,7 +24,7 @@
     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"
@@ -42,7 +42,6 @@
             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>
 
@@ -66,8 +65,12 @@
             android:layout_gravity="center_horizontal" />
 
         <include
+            android:id="@+id/app_info_drop_target_bar"
+            layout="@layout/drop_target_bar_horz_info" />
+
+        <include
             android:id="@+id/search_drop_target_bar"
-            layout="@layout/search_drop_target_bar" />
+            layout="@layout/drop_target_bar_horz_search" />
 
         <include layout="@layout/widgets_view"
             android:id="@+id/widgets_view"
@@ -80,12 +83,6 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:visibility="invisible" />
-    </com.android.launcher3.DragLayer>
+    </com.android.launcher3.dragndrop.DragLayer>
 
-    <ViewStub
-        android:id="@+id/launcher_overlay_stub"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:inflatedId="@+id/launcher_overlay"
-        android:layout="@layout/launcher_overlay" />
 </com.android.launcher3.LauncherRootView>
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 2fc62c5..780d645 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -23,7 +23,7 @@
     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"
@@ -41,7 +41,6 @@
             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>
 
@@ -52,8 +51,12 @@
             android:layout_height="match_parent" />
 
         <include
+            android:id="@+id/app_info_drop_target_bar"
+            layout="@layout/drop_target_bar_horz_info" />
+
+        <include
             android:id="@+id/search_drop_target_bar"
-            layout="@layout/search_drop_target_bar" />
+            layout="@layout/drop_target_bar_horz_search" />
 
         <include layout="@layout/overview_panel"
             android:id="@+id/overview_panel"
@@ -78,13 +81,6 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:visibility="invisible" />
-    </com.android.launcher3.DragLayer>
-
-    <ViewStub
-        android:id="@+id/launcher_overlay_stub"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:inflatedId="@+id/launcher_overlay"
-        android:layout="@layout/launcher_overlay" />
+    </com.android.launcher3.dragndrop.DragLayer>
 
 </com.android.launcher3.LauncherRootView>
diff --git a/res/layout-v21/overview_panel.xml b/res/layout-v21/overview_panel.xml
new file mode 100644
index 0000000..fb6b512
--- /dev/null
+++ b/res/layout-v21/overview_panel.xml
@@ -0,0 +1,69 @@
+<?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.
+-->
+<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" >
+
+    <TextView
+        android:id="@+id/wallpaper_button"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:drawablePadding="4dp"
+        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" />
+
+    <TextView
+        android:id="@+id/widget_button"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:drawablePadding="4dp"
+        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" />
+
+    <TextView
+        android:id="@+id/settings_button"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:drawablePadding="4dp"
+        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" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index a677fff..1b843ed 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -22,6 +22,8 @@
     android:id="@+id/apps_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:paddingTop="@dimen/container_bounds_inset"
+    android:paddingBottom="@dimen/container_bounds_inset"
     android:orientation="vertical"
     launcher:revealBackground="@drawable/quantum_panel_shape">
 
@@ -49,7 +51,7 @@
         <!-- DO NOT CHANGE THE ID -->
         <com.android.launcher3.allapps.AllAppsRecyclerView
             android:id="@+id/apps_list_view"
-            android:theme="@style/Theme.Light.CustomOverscroll"
+            android:theme="@style/CustomOverscroll.Light"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:layout_gravity="center_horizontal|top"
diff --git a/res/layout/all_apps_container.xml b/res/layout/all_apps_container.xml
index 626edaf..c0525ee 100644
--- a/res/layout/all_apps_container.xml
+++ b/res/layout/all_apps_container.xml
@@ -27,7 +27,7 @@
     <!-- DO NOT CHANGE THE ID -->
     <com.android.launcher3.allapps.AllAppsRecyclerView
         android:id="@+id/apps_list_view"
-        android:theme="@style/Theme.Light.CustomOverscroll"
+        android:theme="@style/CustomOverscroll.Light"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_gravity="center_horizontal|top"
diff --git a/res/layout/drop_target_bar_horz_info.xml b/res/layout/drop_target_bar_horz_info.xml
new file mode 100644
index 0000000..92a9b22
--- /dev/null
+++ b/res/layout/drop_target_bar_horz_info.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.AppInfoDropTargetBar xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="48dp"
+    android:layout_gravity="center_horizontal|bottom"
+    android:focusable="false" >
+
+    <FrameLayout
+        android:id="@+id/drag_target_bar"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <!-- Info target -->
+
+        <com.android.launcher3.InfoDropTarget
+            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>
+</com.android.launcher3.AppInfoDropTargetBar>
\ No newline at end of file
diff --git a/res/layout/search_drop_target_bar.xml b/res/layout/drop_target_bar_horz_search.xml
similarity index 62%
rename from res/layout/search_drop_target_bar.xml
rename to res/layout/drop_target_bar_horz_search.xml
index 4737ee1..7997801 100644
--- a/res/layout/search_drop_target_bar.xml
+++ b/res/layout/drop_target_bar_horz_search.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-     Copyright (C) 2011 The Android Open Source Project
+     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.
@@ -15,9 +15,11 @@
      limitations under the License.
 -->
 <com.android.launcher3.SearchDropTargetBar 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"
-    android:focusable="false" >
+    android:layout_gravity="center_horizontal|top"
+    android:focusable="false">
 
     <!-- Drag specific targets container -->
 
@@ -28,39 +30,39 @@
         android:layout_gravity="center" >
 
         <FrameLayout
-            style="@style/DropTargetButtonContainer"
+            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/delete_target_label" />
+                android:text="@string/remove_drop_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_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/delete_target_uninstall_label" />
+                android:text="@string/uninstall_drop_target_label" />
         </FrameLayout>
     </LinearLayout>
 
diff --git a/res/layout/drop_target_bar_vert_info.xml b/res/layout/drop_target_bar_vert_info.xml
new file mode 100644
index 0000000..da33d1a
--- /dev/null
+++ b/res/layout/drop_target_bar_vert_info.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.AppInfoDropTargetBar xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="48dp"
+    android:layout_height="match_parent"
+    android:focusable="false" >
+
+    <FrameLayout
+        android:id="@+id/drag_target_bar"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <!-- Info target -->
+        <com.android.launcher3.InfoDropTarget
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_gravity="center_horizontal|bottom"
+            android:gravity="center"
+            android:paddingLeft="14dp"
+            android:paddingRight="14dp"
+            android:textColor="@android:color/white"
+            android:id="@+id/info_target_text" />
+    </FrameLayout>
+
+</com.android.launcher3.AppInfoDropTargetBar>
\ No newline at end of file
diff --git a/res/layout/drop_target_bar_vert_search.xml b/res/layout/drop_target_bar_vert_search.xml
new file mode 100644
index 0000000..d5e41df
--- /dev/null
+++ b/res/layout/drop_target_bar_vert_search.xml
@@ -0,0 +1,56 @@
+<?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.SearchDropTargetBar xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="48dp"
+    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:orientation="vertical"
+        android:layout_gravity="center"
+        android:paddingTop="20dp">
+
+        <!-- Delete target -->
+        <com.android.launcher3.DeleteDropTarget
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:gravity="center"
+            android:paddingLeft="14dp"
+            android:paddingRight="14dp"
+            android:id="@+id/delete_target_text"
+            android:textColor="@android:color/white"
+            android:layout_marginBottom="10dp" />
+
+        <!-- Uninstall target -->
+        <com.android.launcher3.UninstallDropTarget
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:layout_gravity="center"
+            android:gravity="center"
+            android:paddingLeft="14dp"
+            android:paddingRight="14dp"
+            android:id="@+id/uninstall_target_text"
+            android:textColor="@android:color/white"
+            android:layout_marginTop="10dp"/>
+    </LinearLayout>
+
+</com.android.launcher3.SearchDropTargetBar>
\ 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/launcher_overlay.xml b/res/layout/launcher_overlay.xml
deleted file mode 100644
index b35a2d8..0000000
--- a/res/layout/launcher_overlay.xml
+++ /dev/null
@@ -1,20 +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.InsettableFrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" />
diff --git a/res/layout/launcher_overlay_example.xml b/res/layout/launcher_overlay_example.xml
deleted file mode 100644
index 7d92d4f..0000000
--- a/res/layout/launcher_overlay_example.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.
--->
-
-<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"
-    launcher:layout_ignoreInsets="true">
-
-    <FrameLayout
-        android:id="@+id/search_overlay"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="#ff00ff00"
-        android:visibility="invisible" />
-
-    <FrameLayout
-        android:id="@+id/search_box"
-        android:layout_width="match_parent"
-        android:layout_height="48dp"
-        android:layout_marginLeft="8dp"
-        android:layout_marginRight="8dp"
-        android:layout_marginTop="36dp"
-        android:background="#ffff0000" />
-</FrameLayout>
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index 252ebf0..87a4214 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"
@@ -34,7 +34,7 @@
             android:layout_width="20dp"
             android:layout_height="20dp" />
 
-        <com.android.launcher3.FolderPagedView
+        <com.android.launcher3.folder.FolderPagedView
             android:id="@+id/folder_content"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
@@ -81,4 +81,4 @@
 
     </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..d445a7a 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"
@@ -34,7 +34,7 @@
             android:layout_width="20dp"
             android:layout_height="20dp" />
 
-        <com.android.launcher3.FolderPagedView
+        <com.android.launcher3.folder.FolderPagedView
             android:id="@+id/folder_content"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
@@ -82,4 +82,4 @@
 
     </LinearLayout>
 
-</com.android.launcher3.Folder>
+</com.android.launcher3.folder.Folder>
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index 1d593df..b7f76a6 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -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">
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index c51ec80..e55f6f0 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -22,6 +22,8 @@
     android:id="@+id/widgets_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:paddingTop="@dimen/container_bounds_inset"
+    android:paddingBottom="@dimen/container_bounds_inset"
     android:descendantFocusability="afterDescendants"
     launcher:revealBackground="@drawable/quantum_panel_shape_dark">
 
@@ -44,7 +46,7 @@
 
         <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" />
     </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 6ea6f4a..6285171 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Raak en hou om \'n legstuk op te tel."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Tik 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Tik 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>
     <string name="widget_button_text" msgid="2880537293434387943">"Legstukke"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Verwyder"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index ad9c7f9..f862e2c 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"ፍርግም ለማንሳት ይንኩ እና ይያዙት"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"ንዑስ ፕሮግራም ለማንሳት መታ አድርገው ይያዙት።"</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"በተወዳጆች መሣቢያ ውስጥ ተጨማሪ ቦታ የለም"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"መተግበሪያዎች"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"ለማበጀት ጀርባውን መታ አድርገው ይያዙት"</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="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="abandoned_clean_this" msgid="7610119707847920412">"አስወግድ"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 9d2c42b..2143539 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"المس مع الاستمرار لاختيار إحدى الأدوات."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"المس مع الاستمرار لاختيار إحدى الأدوات."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"لا يوجد المزيد من الحقول في علبة المفضلة"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"التطبيقات"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"المس مع الاستمرار الخلفية لتخصيصها"</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="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="abandoned_clean_this" msgid="7610119707847920412">"إزالة"</string>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index 5256115..6e05132 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Vidceti götürmək üçün toxunub saxlayın."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Vidceti götürmək üçün toxunaraq 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"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>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidcet"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Naməlum"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Yığışdır"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 9c96cd8..c85bf6b 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Докоснете и задръжте за избор на приспособление."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Докоснете и задръжте за избор на приспособление."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Няма повече място в областта с любимите"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"Приложения"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Докоснете и задръжте фона, за да го персонализирате"</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="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="abandoned_clean_this" msgid="7610119707847920412">"Премахване"</string>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index 5a81ed4..29c218f 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"একটি উইজেট তুলতে তা স্পর্শ করে ধরে রাখুন৷"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"একটি উইজেট বেছে নিতে আলতো চেপে ধরে রাখুন৷"</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"পছন্দসই ট্রে-তে আর কোনো জায়গা নেই"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"অ্যাপ্লিকেশানগুলি"</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>
@@ -55,22 +55,23 @@
     <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_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_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_description" msgid="6569028007301925917">"কাস্টমাইজ করার জন্য পটভূমিতে আলতো চেপে ধরে রাখুন"</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="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="abandoned_clean_this" msgid="7610119707847920412">"সরান"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 05932a9..9f72b95 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Mantén premut un widget per triar-lo."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Toca i 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Toca i mantén premut el fons per personalitzar-lo"</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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Desconegut"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Suprimeix"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 373ae83..8b3a8ed 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Widget vyberete dotykem a podržením."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Widget vyberete klepnutím 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgety"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Neznámé"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstranit"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 3654b72..e69871a 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Tryk på en widget, og hold den nede for at vælge."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Ukendt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjern"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index a375ecd..186a06a 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Zum Hinzufügen Widget berühren und halten"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Widget zum Hinzufügen antippen 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Hintergrund zum Anpassen antippen und halten"</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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Unbekannt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Entfernen"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 12a3157..4f4e15f 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Αγγίξτε παρατεταμένα για να πάρετε ένα γραφ.στοιχ."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Πατήστε παρατεταμένα για να πάρετε ένα γραφ.στοιχ."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Δεν υπάρχει επιπλέον χώρος στην περιοχή Αγαπημένα"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"Εφαρμογές"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Πατήστε παρατεταμένα το παρασκήνιο για προσαρμογή"</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="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="abandoned_clean_this" msgid="7610119707847920412">"Κατάργηση"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index f8873a5..09fc3a2 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Touch &amp; hold to pick up a widget."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Tap &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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Tap &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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index f8873a5..09fc3a2 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Touch &amp; hold to pick up a widget."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Tap &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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Tap &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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index f8873a5..09fc3a2 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Touch &amp; hold to pick up a widget."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Tap &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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Tap &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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index cc58b46..dd18c1e 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Mantén presionado el widget que desees elegir."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Mantén presionado para elegir un widget."</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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index c9c6854..579dd84 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Mantén pulsado el widget que quieras seleccionar."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index 053791d..371e632 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Vidina valimiseks vajutage ja hoidke seda all."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Vidina valimiseks puudutage pikalt."</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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Kohandamiseks puudutage pikalt tausta"</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>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidinad"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Teadmata"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eemalda"</string>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index c85466c..fa19b45 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="5154837155685183344">"Widgeta aukeratzeko, eduki ezazu sakatuta."</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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Pertsonalizatzeko, eduki sakatuta 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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgetak"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Ezezaguna"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Kendu"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 85a379b..148e0cf 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"برای انتخاب ابزارک لمس کنید و نگه دارید."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"برای انتخاب ابزارک، لمس کنید و نگه‌دارید."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"فضای بیشتری در سینی موارد دلخواه وجود ندارد"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"برنامه‌ها"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"برای سفارشی کردن، پس‌زمینه را لمس کنید و نگه‌دارید"</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="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="abandoned_clean_this" msgid="7610119707847920412">"حذف"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index c27310b..f04896a 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Valitse widget painamalla sitä pitkään."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Valitse widget koskettamalla 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Muokkaa taustaa koskettamalla 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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgetit"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Tuntematon"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Poista"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index bb0f1a0..b323b82 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Maintenez un doigt sur le widget pour l\'ajouter."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Maintenez un doigt sur le fond d\'écran pour le 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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Supprimer"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index f189c63..e6d7a20 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"App. de manière prolongée pour sélectionner widget."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Supprimer"</string>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index ad25e84..f402072 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Mantén premido un widget para seleccionalo."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Mantén tocado 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Mantén tocado o segundo plano para personalizalo"</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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Descoñecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index 1cd6768..385d41a 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"વિજેટ ચૂંટવા માટે ટચ કરો અને પકડી રાખો."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"વિજેટ ચૂંટવા માટે ટૅપ કરીને આંગળી દાબેલી રાખો."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"મનપસંદ ટ્રે પર વધુ જગ્યા નથી"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"એપ્લિકેશનો"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"કસ્ટમાઇઝ કરવા માટે પૃષ્ઠભૂમિને ટૅપ કરીને આંગળી દાબેલી રાખો"</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="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="abandoned_clean_this" msgid="7610119707847920412">"દૂર કરો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index d2de54c..5e99e61 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"विजेट को चुनने के लिए स्‍पर्श करके रखें."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"कोई विजेट चुनने के लिए टैप करके रखें."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"पसंदीदा ट्रे में और स्थान नहीं है"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"ऐप्लिकेशन"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"पृष्ठभूमि कस्टमाइज़ करने के लिए टैप करके रखें"</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="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="abandoned_clean_this" msgid="7610119707847920412">"निकालें"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 427e5c8..8fe238f 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Dodirnite i držite kako biste podigli widget."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Dodirnite i zadržite da 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Dodirnite i zadržite pozadinu da biste je prilagodili"</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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgeti"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index b9a5b61..d750866 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Modul felvételéhez érintse meg, és tartsa lenyomva"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Modul felvételéhez koppintson, é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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Koppintson é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>
     <string name="widget_button_text" msgid="2880537293434387943">"Modulok"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Ismeretlen"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eltávolítás"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 74d98d6..a17216c 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Հպեք և պահեք՝ վիջեթն ընտրելու համար:"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Հպեք և պահեք՝ վիջեթ ընտրելու համար:"</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ընտրյալների ցուցակում այլևս ազատ տեղ չկա"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"Ծրագրեր"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Հարմարեցնելու համար հպեք ֆոնին և պահեք"</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="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="abandoned_clean_this" msgid="7610119707847920412">"Հեռացնել"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 1dda24b..c9403c2 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Sentuh lama untuk memilih widget."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Sentuh lama 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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Tidak dikenal"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Buang"</string>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index fd6977c..86f42e8 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Haltu fingri á græju til að grípa hana."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"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>
     <string name="widget_button_text" msgid="2880537293434387943">"Græjur"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Óþekkt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjarlægja"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index a01bc9d..9020719 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Tocca e tieni premuto per scegliere un widget."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Sconosciuto"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Rimuovi"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 137800e..1e03cf5 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"גע נגיעה רציפה בווידג\'ט כדי לבחור בו."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"הקש הקשה רציפה על הווידג\'ט כדי לבחור בו."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"אין עוד מקום במגש המועדפים"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"אפליקציות"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"הקשה רציפה על הרקע לצורך התאמה אישית"</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="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="abandoned_clean_this" msgid="7610119707847920412">"הסר"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index e3c3194..fb06f21 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"ウィジェットを追加するには押し続けます。"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"ウィジェットを追加するには押し続けます。"</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"お気に入りトレイに空きスペースがありません"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"アプリ"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"カスタマイズするにはバックグラウンドを押し続けます"</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>
     <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="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"削除"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 288f0ba..b147c03 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"შეეხეთ და დააყოვნეთ ვიჯეტის ასარჩევად."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"შეეხეთ ხანგრძლივად ვიჯეტის ასარჩევად."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"რჩეულების თაროზე ადგილი არ არის"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"აპები"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"მოსარგებად, ხანგრძლივად შეეხეთ ფონს."</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>
     <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="package_state_unknown" msgid="7592128424511031410">"უცნობი"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ამოშლა"</string>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index b633790..61a5dcc 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Виджетті таңдау үшін түртіп, мықтап ұстаңыз."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Виджетті таңдау үшін оны түртіп, ұстап тұрыңыз."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Қалаулылар науасында орын қалмады"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"Қолданбалар"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Теңшеу үшін фонды түртіп, ұстап тұрыңыз"</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="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="abandoned_clean_this" msgid="7610119707847920412">"Алып тастау"</string>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index 3cf2599..6f3af8b 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"ប៉ះ &amp; សង្កត់ ដើម្បី​ជ្រើស​ធាតុ​ក្រាហ្វិក។"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"ប៉ះ និងសង្កត់ឲ្យជាប់ដើម្បីជ្រើសធាតុក្រាហ្វិក"</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"គ្មាន​បន្ទប់​​ក្នុង​ថាស​និយម​ប្រើ"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"កម្មវិធី"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"ប៉ះ និងសង្កត់ផ្ទៃខាងក្រោយឲ្យជាប់ដើម្បីប្ដូរតាមបំណង"</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="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="abandoned_clean_this" msgid="7610119707847920412">"លុបចេញ"</string>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index 8766d71..3931359 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"ವಿಜೆಟ್ ಅನ್ನು ಆರಿಸಿಕೊಳ್ಳಲು ಸ್ಪರ್ಶಿಸಿ &amp; ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"ವಿಜೆಟ್ ಆರಿಸಿಕೊಳ್ಳಲು ಹೋಲ್ಡ್ ಮಾಡಿ ಮತ್ತು ಟ್ಯಾಪ್ ಮಾಡಿ."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ಮೆಚ್ಚಿನವುಗಳ ಟ್ರೇನಲ್ಲಿ ಹೆಚ್ಚಿನ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"ಕಸ್ಟಮೈಸ್ ಮಾಡಲು ಹಿನ್ನೆಲೆಯನ್ನು ಹೋಲ್ಡ್‌ ಮಾಡಿ ಮತ್ತು ಟ್ಯಾಪ್ ಮಾಡಿ"</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="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="abandoned_clean_this" msgid="7610119707847920412">"ತೆಗೆದುಹಾಕಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index be5b506..ff0e63f 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"위젯을 선택하려면 길게 터치하세요."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"길게 탭하여 위젯 선택"</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"즐겨찾기 트레이에 더 이상 공간이 없습니다."</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"앱"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"백그라운드를 길게 탭하여 맞춤설정"</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>
     <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="package_state_unknown" msgid="7592128424511031410">"알 수 없음"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"삭제"</string>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index 0d7cb48..8f7aa0a 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Виджетти тандаш үчүн, басып туруңуз"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Виджетти тандаш үчүн таптап, коё бербей туруңуз."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Тандамалдар тайпасында орун калган жок"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"Колдонмолор"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Ыңгайлаштыруу үчүн фонду таптап, коё бербей туруңуз"</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="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="abandoned_clean_this" msgid="7610119707847920412">"Алып салуу"</string>
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 8ed9c26..5c61e55 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"ສຳພັດຄ້າງໄວ້ ເພື່ອຈັບວິດເຈັດ."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"ແຕະຄ້າງໄວ້ເພື່ອເລືອກວິດເຈັດໃດໜຶ່ງ."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ບໍ່ມີບ່ອນຫວ່າງໃນຖາດສຳລັບເກັບສິ່ງທີ່ໃຊ້ເປັນປະຈຳ"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"ແອັບຯ"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"ແຕະທີ່ພາບພື້ນຫລັງຄ້າງໄວ້ເພື່ອປັບແຕ່ງ"</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="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="abandoned_clean_this" msgid="7610119707847920412">"ລຶບ​"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index f20751a..61437b6 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Palieskite ir laikykite, kad pasirinkt. valdiklį."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"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>
     <string name="widget_button_text" msgid="2880537293434387943">"Valdikliai"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Nežinoma"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Pašalinti"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 7e76374..2264675 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Lai izvēlētos logrīku, pieskarieties un turiet to."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Lai izvēlētos logrīku, pieskarieties un turiet."</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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Lai pielāgotu fonu, pieskarieties tam un turiet."</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>
     <string name="widget_button_text" msgid="2880537293434387943">"Logrīki"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Nezināma"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Noņemt"</string>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index 73a9d95..a3b76bb 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Допри и задржи за да се избере виџетот."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Допрете и задржете за да изберете додаток."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Нема повеќе простор на лентата „Омилени“"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"Апликации"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Допрете и задржете на заднината за да приспособите"</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="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="abandoned_clean_this" msgid="7610119707847920412">"Отстрани"</string>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 1169be2..ac4e9ae 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"ഒരു വിജറ്റ് ചേർക്കുന്നതിന് അത് സ്‌പർശിച്ച് പിടിക്കുക."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"ഒരു വിജറ്റ് ചേർക്കുന്നതിന് അത് ടാപ്പുചെയ്ത് പിടിക്കുക."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"പ്രിയപ്പെട്ടവയുടെ ട്രേയിൽ ഒഴിവൊന്നുമില്ല"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"അപ്ലിക്കേഷനുകൾ"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"ഇഷ്‌ടാനുസൃതമാക്കുന്നതിന് പശ്ചാത്തലം ടാപ്പുചെയ്ത് പിടിക്കുക"</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="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="abandoned_clean_this" msgid="7610119707847920412">"നീക്കംചെയ്യുക"</string>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index 46f46fa..e9a2077 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Виджетийг авах бол хүрээд барина уу."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Жижиг хэрэгсэл авахын тулд дараад, хүлээнэ үү."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"\"Дуртай\" трей дээр өөр зай байхгүй байна"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"Апп"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Тохируулахын тулд арын дэлгэцийг дараад, хүлээнэ үү"</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="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="abandoned_clean_this" msgid="7610119707847920412">"Устгах"</string>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index 5d29197..a8ae19b 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"विजेट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"विजेट निवडण्यासाठी टॅप करा आणि धरून ठेवा."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"आवडीच्या ट्रे मध्ये आणखी जागा नाही"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"अॅप्स"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"सानुकूल करण्यासाठी पार्श्वभूमी टॅप करा आणि धरुन ठेवा"</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="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="abandoned_clean_this" msgid="7610119707847920412">"काढा"</string>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 5da4331..e5a15e8 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Sentuh &amp; tahan untuk mengambil widget."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Sentuh &amp; tahan latar belakang untuk menyesuaikan"</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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Tidak diketahui"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Alih keluar"</string>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index 0264c98..ffee1c0 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -26,7 +26,7 @@
     <string name="activity_not_available" msgid="7456344436509528827">"App လက်လှမ်း မမှီပါ"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"ဒေါင်းလုဒ် appကို လုံခြုံရေး မုဒ်ထဲမှာ ပိတ်ထား"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"လုံခြုံရေး မုဒ်ထဲမှာ ဝီဂျက်များကို ပိတ်ထား"</string>
-    <string name="long_press_widget_to_add" msgid="7699152356777458215">"ဝဒ်ဂျက်တစ်ခုကို ကောက်ယူရန် ဖိနှိပ်ထားပါ"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"ဝစ်ဂျက်တစ်ခုကို ရွေးချယ်ရန် တို့၍ &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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"အနှစ်သက်ဆုံးများ ထားရာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"အပ်ပလီကေးရှင်းများ"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"စိတ်တိုင်းကျပြုပြင်ရန် နောက်ခံတို့၍ &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="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="abandoned_clean_this" msgid="7610119707847920412">"ဖယ်ရှားရန်"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 7b5f3b6..be6b644 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Trykk og hold inne for å plukke opp en modul."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Trykk og hold for å legge til 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"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>
     <string name="widget_button_text" msgid="2880537293434387943">"Moduler"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Ukjent"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjern"</string>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index 2abf005..29db37d 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"एउटा विजेटलाई टिप्नको लागि टच गरेर होल्ड गर्नुहोस्।"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"विजेट छनोट गर्नका लागि ट्याप गरी थिचिरहनुहोस्।"</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"मनपर्ने ट्रे अब कुनै ठाँउ छैन"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"अनुप्रयोगहरू"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"अनुकूलन गर्नका लागि पृष्ठभूमिलाई ट्याप गरी थिचिरहनुहोस्"</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="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="abandoned_clean_this" msgid="7610119707847920412">"हटाउनुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index a55aebf..4a22bce 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Blijf aanraken om een widget toe te voegen."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Tik lang 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>
@@ -37,10 +37,10 @@
     <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_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="permlab_install_shortcut" msgid="5632423390354674437">"Snelkoppelingen instellen"</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>
     <string name="permdesc_read_settings" msgid="5833423719057558387">"De app toestaan de instellingen en snelkoppelingen op de startpagina te lezen."</string>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Tik lang op de achtergrond 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="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="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Verwijderen"</string>
diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
index 6f5acff..b8a7b43 100644
--- a/res/values-pa-rIN/strings.xml
+++ b/res/values-pa-rIN/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"ਇੱਕ ਵਿਜੇਟ ਚੁਣਨ ਲਈ ਛੋਹਵੋT &amp; ਹੋਲਡ ਕਰੋ।"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"ਇੱਕ ਵਿਜਿਟ ਨੂੰ ਚੁਣਨ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ।"</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ਮਨਪਸੰਦ ਟ੍ਰੇ ਵਿੱਚ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ।"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"ਐਪਸ"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"ਬੈਕਗ੍ਰਾਊਂਡ ਨੂੰ ਵਿਸ਼ੇਸ਼-ਵਿਉਂਤਬੱਧ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ ਅਤੇ ਦਬਾਈ ਰੱਖੋ"</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="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="abandoned_clean_this" msgid="7610119707847920412">"ਹਟਾਓ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index ab70265..ad9fbf4 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Aby dodać widżet, kliknij go i przytrzymaj."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Kliknij i przytrzymaj tło, by je 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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widżety"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Brak informacji"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Usuń"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 8a469ac..fd6ddee 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Prima sem soltar para escolher um widget."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Toque 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Tocar 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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remover"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index d9fbdef..e542418 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Toque e pressione para selecionar um widget."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Toque e segure 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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 e mantenha pressionado o segundo plano para personalizar"</string>
+    <string name="workspace_cling_longpress_description" msgid="6569028007301925917">"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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Remover"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 8c715f8..eb54ae7 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Atingeți lung un widget pentru a-l alege."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgeturi"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Necunoscut"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminați"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index b4ddae7..f73cdf2 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Чтобы выбрать виджет, нажмите на значок и удерживайте его."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Чтобы выбрать виджет, коснитесь его и удерживайте."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"В разделе \"Избранное\" больше нет места"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"Приложения"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Чтобы выполнить настройку, коснитесь фона и удерживайте его"</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>
     <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="package_state_unknown" msgid="7592128424511031410">"Неизвестно"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Удалить"</string>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index 1fd7df0..89fb60f 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"විජට් එක ස්පර්ශ කර අහුලා ගැනීමට අල්ලාගෙන සිටින්න."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"විජට් එකක් අහුලා ගැනීමට තට්ටු කර අල්ලාගෙන සිටින්න."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ප්‍රියතම දෑ ඇති තැටියේ තවත් ඉඩ නොමැත"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"යෙදුම්"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"අභිරුචිකරණය කිරීමට පසුබිම තට්ටු කර අල්ලාගෙන සිටින්න"</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="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="abandoned_clean_this" msgid="7610119707847920412">"ඉවත් කරන්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 66772c7..de017dc 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Miniaplikáciu pridáte stlačením a podržaním."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Miniaplikáciu pridáte klepnutí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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Pozadie prispôsobíte klepnutím a podržaním"</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>
     <string name="widget_button_text" msgid="2880537293434387943">"Miniaplikácie"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Neznáme"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstrániť"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index d5e1e4f..c386458 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Za izbiro pripomočka se ga dotaknite in pridržite."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Pridržite pripomoček, da ga izberete."</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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Za prilagajanje pridržite ozadje"</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>
     <string name="widget_button_text" msgid="2880537293434387943">"Pripomočki"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Neznano"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstrani"</string>
diff --git a/res/values-sq-rAL/strings.xml b/res/values-sq-rAL/strings.xml
index 6262170..ccd2c9c 100644
--- a/res/values-sq-rAL/strings.xml
+++ b/res/values-sq-rAL/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Prek dhe mbaj shtypur për të zgjedhur një miniaplikacion."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Trokit dhe mbaj prekur 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Trokit dhe mbaj prekur 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>
     <string name="widget_button_text" msgid="2880537293434387943">"Miniaplikacionet"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"I panjohur"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Hiq"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index f193909..82f44b2 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Додирните и задржите да бисте изабрали виџет."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Додирните и задржите да бисте изабрали виџет."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"Нема више простора на траци Омиљено"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"Апликације"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Додирните и задржите позадину да бисте прилагодили"</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="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="abandoned_clean_this" msgid="7610119707847920412">"Уклони"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 10f5ce4..79e8f42 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Tryck länge om du vill flytta en widget."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Tryck länge om du vill lägga till 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgetar"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Okänt"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ta bort"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 2abe824..58c9ef0 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Gusa na ushikilie ili kuteua wijeti."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Gusa na ushikilie ili uteue 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -62,17 +62,18 @@
     <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_description" msgid="6569028007301925917">"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>
     <string name="widget_button_text" msgid="2880537293434387943">"Wijeti"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Yasiyojulikana"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Ondoa"</string>
diff --git a/res/values-sw600dp-land/dimens.xml b/res/values-sw600dp-land/dimens.xml
index 644c891..be16c89 100644
--- a/res/values-sw600dp-land/dimens.xml
+++ b/res/values-sw600dp-land/dimens.xml
@@ -20,6 +20,5 @@
     <dimen name="toolbar_button_horizontal_padding">20dip</dimen>
 
 <!-- Container -->
-     <dimen name="container_max_width">736dp</dimen>
-
+    <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..e5f2d82 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -15,10 +15,10 @@
 -->
 
 <resources>
-    <!-- Container -->
+<!-- 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>
@@ -34,4 +34,10 @@
     <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/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
index 4df1725..6fb2bf6 100644
--- a/res/values-sw768dp-port/dimens.xml
+++ b/res/values-sw768dp-port/dimens.xml
@@ -17,5 +17,4 @@
 <resources>
 <!-- Container -->
     <dimen name="container_max_width">736dp</dimen>
-
-</resources>
\ No newline at end of file
+</resources>
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index 3990286..ecd4cb0 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"விட்ஜெட்டைத் தேர்வுசெய்ய தொட்டுப் பிடிக்கவும்."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"விட்ஜெட்டைச் சேர்க்க, தொட்டுப் பிடித்திருக்கவும்."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"பிடித்தவை ட்ரேயில் இடமில்லை"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"பயன்பாடுகள்"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"தனிப்பயனாக்க, பின்னணியைத் தொட்டுப் பிடித்திருக்கவும்"</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>
     <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="package_state_unknown" msgid="7592128424511031410">"தெரியாதது"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"அகற்று"</string>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index 997e198..402c261 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"విడ్జెట్‌ను ఎంచుకోవడానికి తాకి &amp; నొక్కి పెట్టండి."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"విడ్జెట్‌ను ఎంచుకోవడానికి నొక్కి, ఉంచండి."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ఇష్టమైనవి ట్రేలో ఖాళీ లేదు"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"అనువర్తనాలు"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"అనుకూలీకరించడానికి నేపథ్యాన్ని నొక్కి, ఉంచండి"</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>
     <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="package_state_unknown" msgid="7592128424511031410">"తెలియదు"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"తీసివేయి"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index b90599e..292a95b 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"แตะค้างเพื่อรับวิดเจ็ต"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"แตะค้างไว้เพื่อเลือกวิดเจ็ต"</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"ไม่มีพื้นที่เหลือในถาดรายการโปรด"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"แอป"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"แตะพื้นหลังค้างไว้เพื่อกำหนดค่า"</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>
     <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="package_state_unknown" msgid="7592128424511031410">"ไม่รู้จัก"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"ลบ"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 7da7b13..5de4a9d 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Pindutin nang matagal upang kumuha ng widget."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Pindutin nang matagal upang mag-angat 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Pindutin nang matagal ang background upang mag-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>
     <string name="widget_button_text" msgid="2880537293434387943">"Mga Widget"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Hindi kilala"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Alisin"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 07db876..44feebc 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Widget seçmek için dokunun ve basılı tutun."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Ö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>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget\'lar"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Bilinmiyor"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Kaldır"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 66f2542..b5d81ee 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Натисніть і утримуйте, щоб вибрати віджет."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Натисніть і втримуйте, щоб вибрати віджет."</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"В області \"Вибране\" немає місця"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"Додатки"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Натисніть і втримуйте фон, щоб налаштувати"</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="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="abandoned_clean_this" msgid="7610119707847920412">"Видалити"</string>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index b3d4c29..bbf9d87 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"کوئی ویجیٹ منتخب کرنے کیلئے ٹچ کریں اور پکڑے رہیں۔"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"کوئی ویجیٹ منتخب کرنے کیلئے تھپتھپائیں اور دبائے رکھیں۔"</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"پسندیدہ ٹرے میں مزید کوئی گنجائش نہیں ہے"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"ایپس"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"حسب ضرورت بنانے کیلئے پس منظر کو تھپتھپائیں اور دبائے رکھیں"</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="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="abandoned_clean_this" msgid="7610119707847920412">"ہٹائیں"</string>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 23f82d8..0ae7d7a 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Vidjetni tanlash uchun bosib turing."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Vidjetni tanlab olish 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>
@@ -37,9 +37,9 @@
     <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‘chirish"</string>
-    <string name="info_target_label" msgid="8053346143994679532">"Ilova haqida"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Orqa fonni moslashtirish uchun uni bosib 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>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidjetlar"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Noma’lum"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"O‘chirish"</string>
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..aa5d0a6
--- /dev/null
+++ b/res/values-v21/styles.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.
+*/
+-->
+<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>
+        <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/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 1ecc92e..b3e6234 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Chạm và giữ để chọn tiện ích con."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Nhấn và giữ để chọn tiện ích."</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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Nhấn 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>
     <string name="widget_button_text" msgid="2880537293434387943">"Tiện ích con"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Không xác định"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Xóa"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 93a7fa7..5911c12 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"触摸并按住小部件即可选择。"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"按住小部件即可选择。"</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"收藏栏已满"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"应用"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"按住背景即可进行个性化设置"</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="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="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 41679b5..63e853b 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"輕觸並按住小工具即可選取。"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"按住小工具即可選取。"</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"我的收藏寄存區沒有足夠空間"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"應用程式"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"按住背景即可自訂"</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>
     <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="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index bc3be0c..2c6046f 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"輕觸並按住小工具即可選取。"</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"按住小工具即可選取。"</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>
@@ -37,9 +37,9 @@
     <string name="hotseat_out_of_space" msgid="7448809638125333693">"「我的最愛」匣已無可用空間"</string>
     <string name="all_apps_button_label" msgid="9110807029020582876">"應用程式"</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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"按住背景即可自訂"</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>
     <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="package_state_unknown" msgid="7592128424511031410">"不明"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index a1dec74..eeffe89 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -26,7 +26,7 @@
     <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="long_press_widget_to_add" msgid="7699152356777458215">"Thinta uphinde ubambe ukuze uphakamise iwijethi."</string>
+    <string name="long_press_widget_to_add" msgid="5154837155685183344">"Thepha uphinde ubambe ukuze ukhethe 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>
@@ -37,9 +37,9 @@
     <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_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>
@@ -60,17 +60,18 @@
     <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_description" msgid="6569028007301925917">"Thepha uphinde ubambe ingemuva ukuze wenze ngendlela oyifisayo"</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>
     <string name="widget_button_text" msgid="2880537293434387943">"Amawijethi"</string>
     <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="package_state_unknown" msgid="7592128424511031410">"Akwaziwa"</string>
     <string name="abandoned_clean_this" msgid="7610119707847920412">"Susa"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index c5be9f2..6d8efaa 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -36,49 +36,9 @@
         <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" />
-    </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 +83,8 @@
     <declare-styleable name="InsettableFrameLayout_Layout">
         <attr name="layout_ignoreInsets" format="boolean" />
     </declare-styleable>
+
+    <declare-styleable name="ButtonDropTarget">
+        <attr name="hideParentOnDisable" format="boolean" />
+    </declare-styleable>
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 8a7f627..dc3d4fa 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -37,6 +37,10 @@
     <color name="quantum_panel_bg_color_dark">#FF374248</color>
 
     <color name="outline_color">#FFFFFFFF</color>
+    <color name="launcher_accent_color">#ff009688</color>
+
+    <color name="spring_loaded_panel_color">#40FFFFFF</color>
+    <color name="spring_loaded_highlighted_panel_border_color">#FFF</color>
 
     <!-- Containers -->
     <color name="container_fastscroll_thumb_inactive_color">#009688</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index c8a610d..d689f1b 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -17,17 +17,16 @@
     <bool name="enable_backup">false</bool>
 
 <!-- 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>
 
@@ -41,8 +40,6 @@
          is used for internal (baked-in) padding -->
     <integer name="config_allAppsButtonPaddingPercent">17</integer>
 
-    <integer name="config_workspaceDefaultScreen">0</integer>
-
 <!-- Workspace -->
     <!-- The duration (in ms) of the fade animation on the object outlines, used when
          we are dragging objects around on the home screen. -->
@@ -71,9 +68,12 @@
          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>
 
     <!-- View ID to use for QSB widget -->
     <item type="id" name="qsb_widget" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index af4ae5e..281de08 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -15,8 +15,6 @@
 -->
 
 <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>
@@ -52,18 +50,11 @@
     <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 -->
@@ -76,13 +67,16 @@
     <dimen name="container_fastscroll_popup_size">72dp</dimen>
     <dimen name="container_fastscroll_popup_text_size">48dp</dimen>
 
+    <item name="container_margin" format="fraction" type="fraction">0%</item>
+    <dimen name="container_min_margin">8dp</dimen>
+    <dimen name="container_max_width">0dp</dimen>
+
 <!-- All Apps -->
     <dimen name="all_apps_button_scale_down">0dp</dimen>
     <dimen name="all_apps_grid_view_start_margin">0dp</dimen>
     <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 -->
@@ -107,16 +101,15 @@
     <dimen name="all_apps_search_bar_bg_overflow">-6dp</dimen>
     <dimen name="all_apps_search_bar_divider_width">1dp</dimen>
 
-    <!-- Widget tray -->
-    <dimen name="widget_container_inset">8dp</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 +124,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 +139,16 @@
          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>
 
 <!-- Sizes for managed profile badges -->
     <dimen name="profile_badge_size">24dp</dimen>
@@ -164,4 +164,5 @@
 <!-- Pending widget -->
     <dimen name="pending_widget_min_padding">8dp</dimen>
     <dimen name="pending_widget_elevation">2dp</dimen>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2838a22..4c9d0b5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -24,12 +24,6 @@
     <!-- 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 -->
@@ -47,7 +41,7 @@
 
     <!-- Widgets -->
     <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
-    <string name="long_press_widget_to_add">Touch &amp; hold to pick up a widget.</string>
+    <string name="long_press_widget_to_add">Tap &amp; hold to pick up a widget.</string>
     <!-- Accessibility spoken hint message in widget picker, which allows user to add a widget. Custom action is the label for additional accessibility actions available in this mode [CHAR_LIMIT=100] -->
     <string name="long_accessible_way_to_add">Double-tap &amp; hold to pick up a widget or use custom actions.</string>
     <!-- The format string for the dimensions of a widget in the drawer -->
@@ -78,12 +72,12 @@
          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 />
@@ -145,7 +139,7 @@
     <!-- 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>
+    <string name="workspace_cling_longpress_description">Tap &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>
 
@@ -153,9 +147,9 @@
     <!-- 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 -->
@@ -170,10 +164,16 @@
     <string name="wallpaper_button_text">Wallpapers</string>
     <!-- Text for settings button -->
     <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>
 
     <!-- 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 homescreen rotation</string>
+    <!-- Text explaining when the home screen will get rotated. [CHAR LIMIT=100] -->
+    <string name="allow_rotation_desc">When device 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>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 4eee130..1b7072d 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -18,14 +18,28 @@
 -->
 
 <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>
+
+    <!-- Overscroll effect -->
+    <style name="CustomOverscroll.Light" parent="@android:style/Theme.DeviceDefault">
         <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" parent="@android:style/Theme.DeviceDefault">
         <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>
@@ -56,18 +70,8 @@
         <item name="customShadows">false</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>
-
+    <!-- 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>
@@ -83,6 +87,7 @@
 
     <style name="DropTargetButton" parent="DropTargetButtonBase" />
 
+    <!-- Virtual preloaders -->
     <style name="PreloadIcon">
         <item name="background">@drawable/virtual_preload</item>
         <item name="indicatorSize">4dp</item>
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/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 3b25dca..c431593 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -22,6 +22,8 @@
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.FlagOp;
+import com.android.launcher3.util.StringFilter;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -118,6 +120,21 @@
         }
     }
 
+    /**
+     * Updates the apps for the given packageName and user based on {@param op}.
+     */
+    public void updatePackageFlags(StringFilter pkgFilter, UserHandleCompat user, FlagOp op) {
+        final List<AppInfo> data = this.data;
+        for (int i = data.size() - 1; i >= 0; i--) {
+            AppInfo info = data.get(i);
+            final ComponentName component = info.intent.getComponent();
+            if (info.user.equals(user) && pkgFilter.matches(component.getPackageName())) {
+                info.isDisabled = op.apply(info.isDisabled);
+                modified.add(info);
+            }
+        }
+    }
+
     public void updateIconsAndLabels(HashSet<String> packages, UserHandleCompat user,
             ArrayList<AppInfo> outUpdates) {
         for (AppInfo info : data) {
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 ede6c71..28fd268 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -26,15 +26,14 @@
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.ComponentKey;
+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.
@@ -58,6 +57,11 @@
 
     int flags = 0;
 
+    /**
+     * {@see ShortcutInfo#isDisabled}
+     */
+    int isDisabled = ShortcutInfo.DEFAULT;
+
     AppInfo() {
         itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
     }
@@ -75,10 +79,22 @@
      */
     public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandleCompat user,
             IconCache iconCache) {
+        this(context, info, user, iconCache,
+                UserManagerCompat.getInstance(context).isQuietModeEnabled(user));
+    }
+
+    public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandleCompat user,
+            IconCache iconCache, boolean quietModeEnabled) {
         this.componentName = info.getComponentName();
         this.container = ItemInfo.NO_ID;
-
         flags = initFlags(info);
+        if (PackageManagerHelper.isAppSuspended(info.getApplicationInfo())) {
+            isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+        }
+        if (quietModeEnabled) {
+            isDisabled |= ShortcutInfo.FLAG_DISABLED_QUIET_USER;
+        }
+
         iconCache.getTitleAndIcon(this, info, true /* useLowResIcon */);
         intent = makeLaunchIntent(context, info, user);
         this.user = user;
@@ -103,6 +119,7 @@
         title = Utilities.trim(info.title);
         intent = new Intent(info.intent);
         flags = info.flags;
+        isDisabled = info.isDisabled;
         iconBitmap = info.iconBitmap;
     }
 
@@ -111,8 +128,7 @@
         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 + ")";
+                + " spanX=" + spanX + " spanY=" + spanY + " user=" + user + ")";
     }
 
     /**
@@ -121,7 +137,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());
         }
     }
@@ -143,4 +159,9 @@
             .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
             .putExtra(EXTRA_PROFILE, serialNumber);
     }
+
+    @Override
+    public boolean isDisabled() {
+        return isDisabled != 0;
+    }
 }
diff --git a/src/com/android/launcher3/AppInfoDropTargetBar.java b/src/com/android/launcher3/AppInfoDropTargetBar.java
new file mode 100644
index 0000000..e06f941
--- /dev/null
+++ b/src/com/android/launcher3/AppInfoDropTargetBar.java
@@ -0,0 +1,80 @@
+/*
+ * 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.util.AttributeSet;
+
+import com.android.launcher3.dragndrop.DragController;
+
+public class AppInfoDropTargetBar extends BaseDropTargetBar {
+    private ButtonDropTarget mAppInfoDropTarget;
+
+    public AppInfoDropTargetBar(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AppInfoDropTargetBar(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        // Get the individual components
+        mAppInfoDropTarget = (ButtonDropTarget) mDropTargetBar.findViewById(R.id.info_target_text);
+
+        mAppInfoDropTarget.setDropTargetBar(this);
+    }
+
+    @Override
+    public void setup(Launcher launcher, DragController dragController) {
+        dragController.addDragListener(this);
+
+        dragController.addDragListener(mAppInfoDropTarget);
+        dragController.addDropTarget(mAppInfoDropTarget);
+
+        mAppInfoDropTarget.setLauncher(launcher);
+    }
+
+    @Override
+    public void showDropTargets() {
+        animateDropTargetBarToAlpha(1f, DEFAULT_DRAG_FADE_DURATION);
+    }
+
+    @Override
+    public void hideDropTargets() {
+        animateDropTargetBarToAlpha(0f, DEFAULT_DRAG_FADE_DURATION);
+    }
+
+    private void animateDropTargetBarToAlpha(float alpha, int duration) {
+        resetAnimation(duration);
+        if (duration > 0) {
+            animateAlpha(mDropTargetBar, alpha, DEFAULT_INTERPOLATOR);
+            mCurrentAnimation.start();
+        } else {
+            mDropTargetBar.setAlpha(alpha);
+            AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
+        }
+    }
+
+    @Override
+    public void enableAccessibleDrag(boolean enable) {
+        mAppInfoDropTarget.enableAccessibleDrag(enable);
+    }
+}
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 6b7ff88..daeca3b 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;
@@ -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..f3a2bdc 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -436,7 +436,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));
 
@@ -630,7 +629,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..d0cacd3 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
 import android.util.AttributeSet;
@@ -28,24 +27,15 @@
 /**
  * A base container view, which supports resizing.
  */
-public abstract class BaseContainerView extends FrameLayout implements Insettable {
+public abstract class BaseContainerView extends FrameLayout {
 
-    private final static String TAG = "BaseContainerView";
-
-    // 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;
+    protected final int mHorizontalPadding;
 
     private final Drawable mRevealDrawable;
 
     private View mRevealView;
     private View mContent;
 
-    protected final int mHorizontalPadding;
-
     public BaseContainerView(Context context) {
         this(context, null);
     }
@@ -56,23 +46,16 @@
 
     public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mContainerBoundsInset = getResources().getDimensionPixelSize(R.dimen.container_bounds_inset);
+
+        int width = ((Launcher) context).getDeviceProfile().availableWidthPx;
+        mHorizontalPadding = DeviceProfile.getContainerPadding(context, width);
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.BaseContainerView, defStyleAttr, 0);
-        mRevealDrawable = a.getDrawable(R.styleable.BaseContainerView_revealBackground);
+        mRevealDrawable = new InsetDrawable(
+                a.getDrawable(R.styleable.BaseContainerView_revealBackground),
+                mHorizontalPadding, 0, mHorizontalPadding, 0);
         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);
-        } else {
-            mHorizontalPadding = Math.max(minMargin,
-                    (int) getResources().getFraction(R.fraction.container_margin, width, 1));
-        }
     }
 
     @Override
@@ -81,68 +64,15 @@
 
         mContent = findViewById(R.id.main_content);
         mRevealView = findViewById(R.id.reveal_view);
-    }
 
-    @Override
-    final public void setInsets(Rect insets) {
-        mInsets.set(insets);
-        updateBackgroundAndPaddings();
-    }
-
-    /**
-     * 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();
-            }
-        });
-    }
-
-    /**
-     * 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);
+        mRevealView.setBackground(mRevealDrawable.getConstantState().newDrawable());
+        mContent.setBackground(mRevealDrawable);
 
         // 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;
     }
diff --git a/src/com/android/launcher3/BaseDropTargetBar.java b/src/com/android/launcher3/BaseDropTargetBar.java
new file mode 100644
index 0000000..9b38623
--- /dev/null
+++ b/src/com/android/launcher3/BaseDropTargetBar.java
@@ -0,0 +1,146 @@
+/*
+ * 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.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AccelerateInterpolator;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.dragndrop.DragController;
+
+/**
+ * Base class for drop target bars (where you can drop apps to do actions such as uninstall).
+ */
+public abstract class BaseDropTargetBar extends FrameLayout implements DragController.DragListener {
+    protected static final int DEFAULT_DRAG_FADE_DURATION = 175;
+    protected static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator();
+
+    protected View mDropTargetBar;
+    protected boolean mAccessibilityEnabled = false;
+
+    protected AnimatorSet mCurrentAnimation;
+    protected boolean mDeferOnDragEnd;
+
+    public BaseDropTargetBar(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public BaseDropTargetBar(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mDropTargetBar = findViewById(R.id.drag_target_bar);
+
+        // Create the various fade animations
+        mDropTargetBar.setAlpha(0f);
+    }
+
+    /**
+     * Convenience method to animate the alpha of a view.
+     */
+    protected 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);
+        }
+    }
+
+    protected void resetAnimation(int newAnimationDuration) {
+        // Update the accessibility state
+        AccessibilityManager am = (AccessibilityManager)
+                getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+        mAccessibilityEnabled = am.isEnabled();
+
+        // Cancel any existing animation
+        if (mCurrentAnimation != null) {
+            mCurrentAnimation.cancel();
+            mCurrentAnimation = null;
+        }
+
+        if (newAnimationDuration > 0) {
+            mCurrentAnimation = new AnimatorSet();
+            mCurrentAnimation.setDuration(newAnimationDuration);
+        }
+    }
+
+    /*
+     * DragController.DragListener implementation
+     */
+    @Override
+    public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
+        showDropTargets();
+    }
+
+    /**
+     * 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) {
+            hideDropTargets();
+        } else {
+            mDeferOnDragEnd = false;
+        }
+    }
+
+    public abstract void showDropTargets();
+
+    public abstract void hideDropTargets();
+
+    public abstract void enableAccessibleDrag(boolean enable);
+
+    public abstract void setup(Launcher launcher, DragController dragController);
+
+    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/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index f6c9e3c..5d0f783 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -25,7 +25,6 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 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 +35,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;
@@ -80,10 +81,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;
@@ -131,7 +136,7 @@
         }
 
         mLongPressHelper = new CheckLongPressHelper(this);
-        mStylusEventHelper = new StylusEventHelper(this);
+        mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
 
         mOutlineHelper = HolographicOutlineHelper.obtain(getContext());
         if (mCustomShadowsEnabled) {
@@ -150,7 +155,7 @@
         Bitmap b = info.getIcon(iconCache);
 
         FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(b);
-        if (info.isDisabled != 0) {
+        if (info.isDisabled()) {
             iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
         }
         setIcon(iconDrawable, mIconSize);
@@ -166,7 +171,11 @@
     }
 
     public void applyFromApplicationInfo(AppInfo info) {
-        setIcon(mLauncher.createIconDrawable(info.iconBitmap), mIconSize);
+        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);
@@ -250,11 +259,11 @@
     private void updateIconState() {
         if (mIcon instanceof FastBitmapDrawable) {
             FastBitmapDrawable d = (FastBitmapDrawable) mIcon;
-            if (isPressed() || mStayPressed) {
-                d.animateState(FastBitmapDrawable.State.PRESSED);
-            } else if (getTag() instanceof ShortcutInfo
-                    && ((ShortcutInfo) getTag()).isDisabled != 0) {
+            if (getTag() instanceof ItemInfo
+                    && ((ItemInfo) getTag()).isDisabled()) {
                 d.animateState(FastBitmapDrawable.State.DISABLED);
+            } else if (isPressed() || mStayPressed) {
+                d.animateState(FastBitmapDrawable.State.PRESSED);
             } else {
                 d.animateState(FastBitmapDrawable.State.NORMAL);
             }
@@ -268,7 +277,7 @@
         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;
         }
@@ -309,6 +318,7 @@
     void setStayPressed(boolean stayPressed) {
         mStayPressed = stayPressed;
         if (!stayPressed) {
+            HolographicOutlineHelper.obtain(getContext()).recycleShadowBitmap(mPressedBackground);
             mPressedBackground = null;
         } else {
             if (mPressedBackground == null) {
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 40a4678..43afbe5 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,9 @@
 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.DragView;
 import com.android.launcher3.util.Thunk;
 
 /**
@@ -47,14 +52,18 @@
 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;
+
+    private final boolean mHideParentOnDisable;
 
     protected Launcher mLauncher;
     private int mBottomDragPadding;
-    protected SearchDropTargetBar mSearchDropTargetBar;
+    protected BaseDropTargetBar mDropTargetBar;
 
     /** Whether this drop target is active for the current drag */
     protected boolean mActive;
+    /** 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 +74,26 @@
     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);
+        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)
@@ -104,8 +113,8 @@
         mLauncher = launcher;
     }
 
-    public void setSearchDropTargetBar(SearchDropTargetBar searchDropTargetBar) {
-        mSearchDropTargetBar = searchDropTargetBar;
+    public void setDropTargetBar(BaseDropTargetBar dropTargetBar) {
+        mDropTargetBar = dropTargetBar;
     }
 
     @Override
@@ -189,8 +198,8 @@
         }
     }
 
-	@Override
-    public final void onDragStart(DragSource source, Object info, int dragAction) {
+    @Override
+    public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
         mActive = supportsDrop(source, info);
         mDrawable.setColorFilter(null);
         if (mCurrentColorAnim != null) {
@@ -198,7 +207,8 @@
             mCurrentColorAnim = null;
         }
         setTextColor(mOriginalTextColor);
-        ((ViewGroup) getParent()).setVisibility(mActive ? View.VISIBLE : View.GONE);
+        (mHideParentOnDisable ? ((ViewGroup) getParent()) : this)
+                .setVisibility(mActive ? View.VISIBLE : View.GONE);
     }
 
     @Override
@@ -206,11 +216,12 @@
         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
+                && mLauncher.getDragController().getDistanceDragged() >= mDragDistanceThreshold;
     }
 
     @Override
@@ -232,13 +243,13 @@
         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);
             }
         };
@@ -297,11 +308,6 @@
         return to;
     }
 
-    @Override
-    public void getLocationInDragLayer(int[] loc) {
-        mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
-    }
-
     public void enableAccessibleDrag(boolean enable) {
         setOnClickListener(enable ? this : null);
     }
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 94e3e41..eaa96c9 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;
@@ -33,6 +32,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.TransitionDrawable;
 import android.os.Build;
 import android.os.Parcelable;
@@ -48,10 +48,13 @@
 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.util.ParcelableSparseArray;
 import com.android.launcher3.util.Thunk;
 
@@ -70,17 +73,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;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     @Thunk int mCountX;
+    @ViewDebug.ExportedProperty(category = "launcher")
     @Thunk 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;
@@ -98,12 +107,14 @@
     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 +161,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;
@@ -202,11 +210,16 @@
         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));
 
@@ -272,7 +285,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 +347,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;
@@ -400,7 +413,7 @@
         mIsDragTarget = false;
     }
 
-    boolean isDragTarget() {
+    public boolean isDragTarget() {
         return mIsDragTarget;
     }
 
@@ -420,10 +433,6 @@
         }
     }
 
-    public boolean getIsDragOverlapping() {
-        return mIsDragOverlapping;
-    }
-
     public void disableJailContent() {
         mJailContent = false;
     }
@@ -450,6 +459,10 @@
                 (ParcelableSparseArray) parcelable : new ParcelableSparseArray();
     }
 
+    public boolean getIsDragOverlapping() {
+        return mIsDragOverlapping;
+    }
+
     @Override
     protected void onDraw(Canvas canvas) {
         if (!mIsDragTarget) {
@@ -469,12 +482,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);
             }
         }
 
@@ -495,88 +505,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 +578,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.
@@ -810,7 +799,7 @@
         return (float) Math.hypot(x - mTmpPoint[0], y - mTmpPoint[1]);
     }
 
-    int getCellWidth() {
+    public int getCellWidth() {
         return mCellWidth;
     }
 
@@ -1070,53 +1059,57 @@
 
             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 +1139,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.
@@ -1338,9 +1314,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
@@ -1424,10 +1398,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 +1411,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,14 +1430,11 @@
                 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));
@@ -1470,32 +1442,32 @@
                     case LEFT:
                         int left = cs.x;
                         for (int j = cs.y; j < cs.y + cs.spanY; j++) {
-                            if (left < edge[j] || edge[j] < 0) {
-                                edge[j] = left;
+                            if (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;
+                            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;
+                            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;
+                            if (bottom > bottomEdge[j]) {
+                                bottomEdge[j] = bottom;
                             }
                         }
                         break;
@@ -1506,33 +1478,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) {
+                        if (leftEdge[i] == cs.x + cs.spanX) {
                             return true;
                         }
                     }
                     break;
                 case RIGHT:
                     for (int i = cs.y; i < cs.y + cs.spanY; i++) {
-                        if (edge[i] == cs.x) {
+                        if (rightEdge[i] == cs.x) {
                             return true;
                         }
                     }
                     break;
                 case TOP:
                     for (int i = cs.x; i < cs.x + cs.spanX; i++) {
-                        if (edge[i] == cs.y + cs.spanY) {
+                        if (topEdge[i] == cs.y + cs.spanY) {
                             return true;
                         }
                     }
                     break;
                 case BOTTOM:
                     for (int i = cs.x; i < cs.x + cs.spanX; i++) {
-                        if (edge[i] == cs.y) {
+                        if (bottomEdge[i] == cs.y) {
                             return true;
                         }
                     }
@@ -1584,48 +1559,6 @@
             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;
@@ -2234,17 +2167,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();
         }
     }
 
@@ -2261,6 +2191,15 @@
                 mOccupied[i][j] = mTmpOccupied[i][j];
             }
         }
+
+        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 +2208,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) {
@@ -2623,7 +2566,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);
     }
 
@@ -2867,10 +2810,10 @@
 
         // 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;
 
@@ -2967,7 +2910,7 @@
     //    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;
+        public View cell;
         int cellX = -1;
         int cellY = -1;
         int spanX;
@@ -2996,6 +2939,26 @@
         return Utilities.findVacantCell(outXY, spanX, spanY, mCountX, mCountY, mOccupied);
     }
 
+    /**
+     * 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;
+    }
+
     public boolean isRegionVacant(int x, int y, int spanX, int spanY) {
         int x2 = x + spanX - 1;
         int y2 = y + spanY - 1;
diff --git a/src/com/android/launcher3/ClickShadowView.java b/src/com/android/launcher3/ClickShadowView.java
index e31d7f7..e2bc6ba 100644
--- a/src/com/android/launcher3/ClickShadowView.java
+++ b/src/com/android/launcher3/ClickShadowView.java
@@ -22,6 +22,7 @@
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.view.View;
+import android.view.ViewDebug;
 import android.view.ViewGroup;
 
 public class ClickShadowView extends View {
@@ -32,7 +33,9 @@
 
     private final Paint mPaint;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     private final float mShadowOffset;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private final float mShadowPadding;
 
     private Bitmap mBitmap;
diff --git a/src/com/android/launcher3/CommonAppTypeParser.java b/src/com/android/launcher3/CommonAppTypeParser.java
index 001e044..a4a92b6 100644
--- a/src/com/android/launcher3/CommonAppTypeParser.java
+++ b/src/com/android/launcher3/CommonAppTypeParser.java
@@ -68,7 +68,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);
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index edaf525..f24e00b 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -19,10 +19,13 @@
 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.folder.Folder;
 import com.android.launcher3.util.FlingAnimation;
 import com.android.launcher3.util.Thunk;
 
@@ -45,20 +48,37 @@
         setDrawable(R.drawable.ic_remove_launcher);
     }
 
-    public static boolean supportsDrop(Object info) {
+    @Override
+    public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
+        super.onDragStart(source, info, dragAction);
+        setTextBasedOnDragSource(source);
+    }
+
+    /** @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 +100,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..9ab5611 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -31,7 +31,8 @@
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewGroup.MarginLayoutParams;
 import android.widget.FrameLayout;
-import android.widget.LinearLayout;
+
+import com.android.launcher3.config.FeatureFlags;
 
 public class DeviceProfile {
 
@@ -83,6 +84,7 @@
     // Folder
     public int folderBackgroundOffset;
     public int folderIconSizePx;
+    public int folderIconPreviewPadding;
     public int folderCellWidthPx;
     public int folderCellHeightPx;
 
@@ -242,7 +244,8 @@
         FontMetrics fm = textPaint.getFontMetrics();
         cellWidthPx = iconSizePx;
         cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top);
-        final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale);
+        final float scaleDps = !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? 0f
+                : res.getDimensionPixelSize(R.dimen.dragViewScale);
         dragViewScale = (iconSizePx + scaleDps) / iconSizePx;
 
         // Hotseat
@@ -259,6 +262,7 @@
         folderCellHeightPx = cellHeightPx + edgeMarginPx;
         folderBackgroundOffset = -edgeMarginPx;
         folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
+        folderIconPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
     }
 
     /**
@@ -326,8 +330,20 @@
         return bounds;
     }
 
+    public Point getCellSize() {
+        Point result = new Point();
+        // Since we are only concerned with the overall padding, layout direction does
+        // not matter.
+        Rect padding = getWorkspacePadding(false /* isLayoutRtl */ );
+        result.x = calculateCellWidth(availableWidthPx - padding.left - padding.right,
+                inv.numColumns);
+        result.y = calculateCellHeight(availableHeightPx - padding.top - padding.bottom,
+                inv.numRows);
+        return result;
+    }
+
     /** Returns the workspace padding in the specified orientation */
-    Rect getWorkspacePadding(boolean isLayoutRtl) {
+    public Rect getWorkspacePadding(boolean isLayoutRtl) {
         Rect searchBarBounds = getSearchBarBounds(isLayoutRtl);
         Rect padding = new Rect();
         if (isVerticalBarLayout()) {
@@ -388,13 +404,13 @@
     }
 
     // The rect returned will be extended to below the system ui that covers the workspace
-    Rect getHotseatRect() {
+    public boolean isInHotseatRect(int x, int y) {
         if (isVerticalBarLayout()) {
-            return new Rect(availableWidthPx - normalHotseatBarHeightPx, 0,
-                    Integer.MAX_VALUE, availableHeightPx);
+            return (x >= (availableWidthPx - hotseatBarHeightPx))
+                    && (y >= 0) && (y <= availableHeightPx);
         } else {
-            return new Rect(0, availableHeightPx - hotseatBarHeightPx,
-                    availableWidthPx, Integer.MAX_VALUE);
+            return (x >= 0) && (x <= availableWidthPx)
+                    && (y >= (availableHeightPx - hotseatBarHeightPx));
         }
     }
 
@@ -410,7 +426,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;
     }
 
@@ -456,23 +472,14 @@
         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;
-        }
         searchBar.setLayoutParams(lp);
 
+        // Layout the app info bar space
+        View appInfoBar = launcher.getAppInfoDropTargetBar();
+        lp = (FrameLayout.LayoutParams) appInfoBar.getLayoutParams();
+        lp.bottomMargin = hotseatBarHeightPx;
+        appInfoBar.setLayoutParams(lp);
+
         // Layout the workspace
         PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace);
         lp = (FrameLayout.LayoutParams) workspace.getLayoutParams();
@@ -539,7 +546,6 @@
         // 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;
 
@@ -548,7 +554,7 @@
             int maxWidth = totalItemWidth + (visibleChildCount-1) * overviewModeBarSpacerWidthPx;
 
             lp.width = Math.min(availableWidthPx, maxWidth);
-            lp.height = overviewButtonBarHeight;
+            lp.height = getOverviewModeButtonBarHeight();
             overviewMode.setLayoutParams(lp);
 
             if (lp.width > totalItemWidth && visibleChildCount > 1) {
@@ -588,4 +594,19 @@
                 ? Math.min(widthPx, heightPx)
                 : Math.max(widthPx, heightPx);
     }
+
+
+    public static final int getContainerPadding(Context context, int availableWidth) {
+        Resources res = context.getResources();
+
+        int maxSize = res.getDimensionPixelSize(R.dimen.container_max_width);
+        int minMargin = res.getDimensionPixelSize(R.dimen.container_min_margin);
+
+        if (maxSize > 0) {
+            return Math.max(minMargin, (availableWidth - maxSize) / 2);
+        } else {
+            return Math.max(minMargin,
+                    (int) res.getFraction(R.fraction.container_margin, availableWidth, 1));
+        }
+    }
 }
diff --git a/src/com/android/launcher3/DragSource.java b/src/com/android/launcher3/DragSource.java
index 2a1346e..da32d82 100644
--- a/src/com/android/launcher3/DragSource.java
+++ b/src/com/android/launcher3/DragSource.java
@@ -37,7 +37,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..90b8f1c 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;
 
@@ -49,7 +49,7 @@
         public DragView dragView = null;
 
         /** The data associated with the object being dragged */
-        public Object dragInfo = null;
+        public ItemInfo dragInfo = null;
 
         /** Where the drag originated */
         public DragSource dragSource = null;
@@ -152,7 +152,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/ExtendedEditText.java b/src/com/android/launcher3/ExtendedEditText.java
index c7b64ec..bf4551b 100644
--- a/src/com/android/launcher3/ExtendedEditText.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.DragEvent;
 import android.view.KeyEvent;
 import android.widget.EditText;
 
@@ -62,4 +63,10 @@
         }
         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;
+    }
 }
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..f99c08a 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;
diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java
index 58b38eb..a835d99 100644
--- a/src/com/android/launcher3/FocusIndicatorView.java
+++ b/src/com/android/launcher3/FocusIndicatorView.java
@@ -16,7 +16,7 @@
 
 package com.android.launcher3;
 
-import android.animation.ObjectAnimator;
+import android.animation.Animator;
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.graphics.Canvas;
@@ -37,7 +37,7 @@
     private final int[] mIndicatorPos = new int[2];
     private final int[] mTargetViewPos = new int[2];
 
-    private ObjectAnimator mCurrentAnimation;
+    private Animator mCurrentAnimation;
     private ViewAnimState mTargetState;
 
     private View mLastFocusedView;
@@ -117,12 +117,12 @@
 
             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));
+                mCurrentAnimation = new LauncherViewPropertyAnimator(this)
+                        .alpha(1)
+                        .translationX(mTargetState.x)
+                        .translationY(mTargetState.y)
+                        .scaleX(mTargetState.scaleX)
+                        .scaleY(mTargetState.scaleY);
             } else {
                 applyState(nextState);
                 mCurrentAnimation = LauncherAnimUtils.ofPropertyValuesHolder(this,
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..6c9d969 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,7 +105,7 @@
 
     }
 
-    void addListener(FolderListener listener) {
+    public void addListener(FolderListener listener) {
         listeners.add(listener);
     }
 
@@ -116,9 +115,9 @@
         }
     }
 
-    void itemsChanged() {
+    public void itemsChanged(boolean animate) {
         for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).onItemsChanged();
+            listeners.get(i).onItemsChanged(animate);
         }
     }
 
@@ -128,11 +127,11 @@
         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();
+        public void onItemsChanged(boolean animate);
     }
 
     @Override
@@ -140,7 +139,7 @@
         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) + ")";
+                + " spanY=" + spanY + ")";
     }
 
     public boolean hasOption(int optionFlag) {
diff --git a/src/com/android/launcher3/HolographicOutlineHelper.java b/src/com/android/launcher3/HolographicOutlineHelper.java
index 0d68e33..6ea06e9 100644
--- a/src/com/android/launcher3/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/HolographicOutlineHelper.java
@@ -38,14 +38,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 +62,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) {
@@ -171,22 +166,46 @@
         int key = (bitmapWidth << 16) | bitmapHeight;
         Bitmap cache = mBitmapCache.get(key);
         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);
         } else {
             mCanvas.setBitmap(cache);
-            mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
+            mCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
         }
 
-        mCanvas.save(Canvas.MATRIX_SAVE_FLAG);
+        int saveCount = mCanvas.save();
         mCanvas.scale(view.getScaleX(), view.getScaleY());
         mCanvas.translate(-rect.left, -rect.top);
         icon.draw(mCanvas);
-        mCanvas.restore();
+        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 = mBitmapCache.get(key);
+        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..7e1ecf5 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.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.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.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, Insettable {
 
     private CellLayout mContent;
 
     private Launcher mLauncher;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     private int mAllAppsButtonRank;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     private final boolean mHasVerticalHotseat;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private Rect mInsets = new Rect();
+
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private int mBackgroundColor;
+    @ViewDebug.ExportedProperty(category = "launcher")
+    private ColorDrawable mBackground;
+
     public Hotseat(Context context) {
         this(context, null);
     }
@@ -50,9 +69,11 @@
         super(context, attrs, defStyle);
         mLauncher = (Launcher) context;
         mHasVerticalHotseat = mLauncher.getDeviceProfile().isVerticalBarLayout();
+        mBackground = new ColorDrawable();
+        setBackground(mBackground);
     }
 
-    CellLayout getLayout() {
+    public CellLayout getLayout() {
         return mContent;
     }
 
@@ -154,7 +175,52 @@
     }
 
     @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.itemType = LauncherLogProto.APP_ICON;
+        target.gridX = info.cellX;
+        target.gridY = info.cellY;
+        targetParent.containerType = LauncherLogProto.HOTSEAT;
+    }
+
+    //Overridden so that the background color extends behind the navigation buttons.
+    @Override
+    public void setInsets(Rect insets) {
+        int rightInset = insets.right - mInsets.right;
+        int bottomInset = insets.bottom - mInsets.bottom;
+        mInsets.set(insets);
+        LayoutParams lp = (LayoutParams) getLayoutParams();
+        if (mHasVerticalHotseat) {
+            setPadding(getPaddingLeft(), getPaddingTop(),
+            getPaddingRight() + rightInset, getPaddingBottom());
+            if (lp.width != LayoutParams.MATCH_PARENT && lp.width != LayoutParams.WRAP_CONTENT) {
+                lp.width += rightInset;
+            }
+        } else {
+            setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
+            getPaddingBottom() + bottomInset);
+            if (lp.height != LayoutParams.MATCH_PARENT && lp.height != LayoutParams.WRAP_CONTENT) {
+                lp.height += bottomInset;
+            }
+        }
+    }
+
+    public void updateColor(ExtractedColors extractedColors, boolean animate) {
+        if (!mHasVerticalHotseat) {
+            int color = extractedColors.getColor(ExtractedColors.HOTSEAT_INDEX, Color.TRANSPARENT);
+            if (!animate) {
+                setBackgroundColor(color);
+            } else {
+                ValueAnimator animator = ValueAnimator.ofInt(mBackgroundColor, color);
+                animator.setEvaluator(new ArgbEvaluator());
+                animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                    @Override
+                    public void onAnimationUpdate(ValueAnimator animation) {
+                        mBackground.setColor((Integer) animation.getAnimatedValue());
+                    }
+                });
+                animator.start();
+            }
+            mBackgroundColor = color;
+        }
     }
 }
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 05ad538..effecaf 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -55,7 +55,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;
 
@@ -89,6 +88,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 +107,6 @@
     private final int mPackageBgColor;
     private final BitmapFactory.Options mLowResOptions;
 
-    private String mSystemState;
-    private Bitmap mLowResBitmap;
     private Canvas mLowResCanvas;
     private Paint mLowResPaint;
 
@@ -118,7 +116,12 @@
         mUserManager = UserManagerCompat.getInstance(mContext);
         mLauncherApps = LauncherAppsCompat.getInstance(mContext);
         mIconDpi = inv.fillResIconDpi;
-        mIconDb = new IconDB(context);
+        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());
 
@@ -128,7 +131,6 @@
         // 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 +242,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 +316,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 +384,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 +414,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() {
 
@@ -505,16 +511,16 @@
     }
 
     /**
-     * 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.iconBitmap = getNonNullIcon(entry, infoInOut.user);
+        infoInOut.title = Utilities.trim(entry.title);
+        infoInOut.usingLowResIcon = entry.isLowResIcon;
+        infoInOut.contentDescription = entry.contentDescription;
     }
 
     public synchronized Bitmap getDefaultIcon(UserHandleCompat user) {
@@ -544,7 +550,8 @@
             if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
                 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 +632,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 +686,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);
@@ -788,15 +801,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 = 9;
 
         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";
@@ -809,8 +818,10 @@
         private final static String COLUMN_LABEL = "label";
         private final static String COLUMN_SYSTEM_STATE = "system_state";
 
-        public IconDB(Context context) {
-            super(context, LauncherFiles.APP_ICONS_DB, RELEASE_VERSION, TABLE_NAME);
+        public IconDB(Context context, int iconPixelSize) {
+            super(context, LauncherFiles.APP_ICONS_DB,
+                    (RELEASE_VERSION << 16) + iconPixelSize,
+                    TABLE_NAME);
         }
 
         @Override
@@ -829,34 +840,40 @@
         }
     }
 
-    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(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..191becf 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -16,13 +16,18 @@
 
 package com.android.launcher3;
 
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 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);
@@ -41,7 +46,19 @@
         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 +66,33 @@
             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) {
+        return info instanceof AppInfo || info instanceof ShortcutInfo
+                || info instanceof PendingAddItemInfo || info instanceof LauncherAppWidgetInfo;
     }
 }
diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
index 7343bf6..f4bfa45 100644
--- a/src/com/android/launcher3/InsettableFrameLayout.java
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -5,12 +5,14 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 public class InsettableFrameLayout extends FrameLayout implements
     ViewGroup.OnHierarchyChangeListener, Insettable {
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     protected Rect mInsets = new Rect();
 
     public InsettableFrameLayout(Context context, AttributeSet attrs) {
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 46b9b7d..921e90c 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -87,7 +87,7 @@
         }
     }
 
-    public static void removeFromInstallQueue(Context context, ArrayList<String> packageNames,
+    public static void removeFromInstallQueue(Context context, HashSet<String> packageNames,
             UserHandleCompat user) {
         if (packageNames.isEmpty()) {
             return;
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index b3a8bbc..0742df9 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -73,17 +73,19 @@
     /**
      * Number of icons inside the hotseat area.
      */
-    int numHotseatIcons;
+    public int numHotseatIcons;
     float hotseatIconSize;
     int defaultLayoutId;
 
     // Derived invariant properties
-    int hotseatAllAppsRank;
+    public int hotseatAllAppsRank;
 
     DeviceProfile landscapeProfile;
     DeviceProfile portraitProfile;
 
-    InvariantDeviceProfile() {
+    public Point defaultWallpaperSize;
+
+    public InvariantDeviceProfile() {
     }
 
     public InvariantDeviceProfile(InvariantDeviceProfile p) {
@@ -166,6 +168,16 @@
                 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() {
@@ -299,4 +311,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 f7e0ea4..1ba09e1 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -24,8 +24,6 @@
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 
-import java.util.Arrays;
-
 /**
  * Represents an item in the launcher.
  */
@@ -35,14 +33,14 @@
      * Intent extra to store the profile. Format: UserHandle
      */
     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 +48,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 +98,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 +107,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 +134,11 @@
     }
 
     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
-     */
-
-    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 +146,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);
 
@@ -194,7 +196,13 @@
     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 + ")";
+            + " spanY=" + spanY + " user=" + user + ")";
+    }
+
+    /**
+     * Whether this item is disabled.
+     */
+    public boolean isDisabled() {
+        return false;
     }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 166acd3..eacf72a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -21,7 +21,6 @@
 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;
@@ -37,12 +36,14 @@
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
+import android.content.ContentValues;
 import android.content.Context;
 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;
@@ -57,7 +58,6 @@
 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;
@@ -75,7 +75,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 +82,10 @@
 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,7 +94,6 @@
 import android.widget.Toast;
 
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.PagedView.PageSwitchListener;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.DefaultAppSearchController;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
@@ -103,43 +101,52 @@
 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.DragView;
+import com.android.launcher3.dynamicui.ExtractedColors;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.logging.LoggerUtils;
+import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.LongArrayMap;
+import com.android.launcher3.logging.FileLog;
 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;
+import java.util.Locale;
 
 /**
  * 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 DEBUG_LOGGING = false;
 
     static final boolean ENABLE_DEBUG_INTENTS = false; // allow DebugIntents to run
 
@@ -165,10 +172,8 @@
      */
     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
@@ -179,18 +184,8 @@
     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: Content Values / parcelable
+    private static final String RUNTIME_STATE_PENDING_ADD_ITEM = "launcher.add_item";
     // Type: parcelable
     private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info";
     // Type: parcelable
@@ -199,29 +194,22 @@
     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";
 
     /** 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;
 
     private boolean mIsSafeModeEnabled;
 
-    LauncherOverlayCallbacks mLauncherOverlayCallbacks = new LauncherOverlayCallbacksImpl();
-    LauncherOverlay mLauncherOverlay;
-    InsettableFrameLayout mLauncherOverlayContainer;
-
     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;
 
@@ -233,8 +221,6 @@
     private final BroadcastReceiver mCloseSystemDialogsReceiver
             = new CloseSystemDialogsIntentReceiver();
 
-    private LayoutInflater mInflater;
-
     @Thunk Workspace mWorkspace;
     private View mLauncherView;
     private View mPageIndicators;
@@ -246,7 +232,7 @@
     private AppWidgetManagerCompat mAppWidgetManager;
     private LauncherAppWidgetHost mAppWidgetHost;
 
-    @Thunk ItemInfo mPendingAddInfo = new ItemInfo();
+    @Thunk final ItemInfo mPendingAddInfo = new ItemInfo();
     private LauncherAppWidgetProviderInfo mPendingAddWidgetInfo;
     private int mPendingAddWidgetId = -1;
 
@@ -259,6 +245,7 @@
     private View mWidgetsButton;
 
     private SearchDropTargetBar mSearchDropTargetBar;
+    private AppInfoDropTargetBar mAppInfoDropTargetBar;
 
     // Main container view for the all apps screen.
     @Thunk AllAppsContainerView mAppsView;
@@ -267,7 +254,6 @@
     @Thunk WidgetsContainerView mWidgetsView;
     @Thunk WidgetsModel mWidgetsModel;
 
-    private boolean mAutoAdvanceRunning = false;
     private AppWidgetHostView mQsb;
 
     private Bundle mSavedState;
@@ -287,11 +273,11 @@
 
     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;
     @Thunk boolean mUserPresent = true;
     private boolean mVisible = false;
     private boolean mHasFocus = false;
@@ -299,35 +285,27 @@
 
     private LauncherClings mClings;
 
-    private static LongArrayMap<FolderInfo> sFolders = new LongArrayMap<>();
-
     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;
+    private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
 
     @Thunk Drawable mWorkspaceBackgroundDrawable;
 
     private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
     private static final boolean DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE = false;
 
-    static final ArrayList<String> sDumpLogs = new ArrayList<String>();
-    static Date sDateStamp = new Date();
-    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.
     private SharedPreferences mSharedPrefs;
@@ -377,8 +355,9 @@
         int appWidgetId;
     }
 
-    private Stats mStats;
-    FocusIndicatorView mFocusHandler;
+    private UserEventDispatcher mUserEventDispatcher;
+
+    public FocusIndicatorView mFocusHandler;
     private boolean mRotationEnabled = false;
 
     @Thunk void setOrientation() {
@@ -390,11 +369,7 @@
         }
     }
 
-    private Runnable mUpdateOrientationRunnable = new Runnable() {
-        public void run() {
-            setOrientation();
-        }
-    };
+    private RotationPrefChangeHandler mRotationPrefChangeHandler;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -433,11 +408,8 @@
         mIconCache = app.getIconCache();
 
         mDragController = new DragController(this);
-        mInflater = getLayoutInflater();
         mStateTransitionAnimation = new LauncherStateTransitionAnimation(this);
 
-        mStats = new Stats(this);
-
         mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
 
         mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
@@ -459,6 +431,11 @@
         app.getInvariantDeviceProfile().portraitProfile.setSearchBarHeight(getSearchBarHeight());
         setupViews();
         mDeviceProfile.layout(this);
+        mExtractedColors = new ExtractedColors();
+        loadExtractedColorsAndColorItems();
+
+        ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
+                .addAccessibilityStateChangeListener(this);
 
         lockAllApps();
 
@@ -488,11 +465,13 @@
         IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
         registerReceiver(mCloseSystemDialogsReceiver, filter);
 
-        mRotationEnabled = Utilities.isRotationAllowedForDevice(getApplicationContext());
+        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(), false);
+            mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext());
+            mRotationPrefChangeHandler = new RotationPrefChangeHandler();
+            mSharedPrefs.registerOnSharedPreferenceChangeListener(mRotationPrefChangeHandler);
         }
 
         // On large interfaces, or on devices that a user has specifically enabled screen rotation,
@@ -501,13 +480,6 @@
 
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onCreate(savedInstanceState);
-            if (mLauncherCallbacks.hasLauncherOverlay()) {
-                ViewStub stub = (ViewStub) findViewById(R.id.launcher_overlay_stub);
-                mLauncherOverlayContainer = (InsettableFrameLayout) stub.inflate();
-                mLauncherOverlay = mLauncherCallbacks.setLauncherOverlayView(
-                        mLauncherOverlayContainer, mLauncherOverlayCallbacks);
-                mWorkspace.setLauncherOverlay(mLauncherOverlay);
-            }
         }
 
         if (shouldShowIntroScreen()) {
@@ -519,12 +491,15 @@
     }
 
     @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 (mExtractedColors != null && Utilities.isNycOrAbove()) {
+            mExtractedColors.load(this);
+            mHotseat.updateColor(mExtractedColors, !mPaused);
         }
     }
 
@@ -537,13 +512,23 @@
         }
     }
 
+    /**
+     * Call this after onCreate to set or clear overlay.
+     */
+    public void setLauncherOverlay(LauncherOverlay overlay) {
+        if (overlay != null) {
+            overlay.setOverlayCallbacks(new LauncherOverlayCallbacksImpl());
+        }
+        mWorkspace.setLauncherOverlay(overlay);
+    }
+
     public boolean setLauncherCallbacks(LauncherCallbacks callbacks) {
         mLauncherCallbacks = callbacks;
         mLauncherCallbacks.setLauncherSearchCallback(new Launcher.LauncherSearchCallbacks() {
             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
@@ -552,7 +537,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();
@@ -590,14 +575,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) {
@@ -636,12 +613,41 @@
         }
     }
 
-    public Stats getStats() {
-        return mStats;
+    /**
+     * 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.
+     */
+    private UserEventDispatcher createUserEventDispatcher() {
+        return new UserEventDispatcher() {
+            @Override
+            public void dispatchUserEvent(LauncherLogProto.LauncherEvent ev, Intent intent) {
+                if (!DEBUG_LOGGING) {
+                    return;
+                }
+                Log.d("UserEvent", String.format(Locale.US,
+                        "action:%s\nchild:%s\nparent:%s\nelapsed container %d ms session %d ms",
+                        LoggerUtils.getActionStr(ev.action),
+                        LoggerUtils.getTargetStr(ev.srcTarget[0]),
+                        LoggerUtils.getTargetStr(ev.srcTarget[1]),
+                        ev.elapsedContainerMillis,
+                        ev.elapsedSessionMillis));
+            }
+        };
     }
 
-    public LayoutInflater getInflater() {
-        return mInflater;
+    public UserEventDispatcher getUserEventDispatcher() {
+        if (mLauncherCallbacks != null) {
+            UserEventDispatcher dispatcher = mLauncherCallbacks.getUserEventDispatcher();
+            if (dispatcher != null) {
+                return dispatcher;
+            }
+        }
+
+        if (mUserEventDispatcher == null) {
+            mUserEventDispatcher = createUserEventDispatcher();
+        }
+        return mUserEventDispatcher;
     }
 
     public boolean isDraggingEnabled() {
@@ -724,7 +730,7 @@
         } 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);
             }
@@ -810,24 +816,22 @@
             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.
-
-        // 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);
+        if (requestCode == REQUEST_CREATE_SHORTCUT) {
+            // Handle custom shortcuts created using ACTION_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);
+                    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);
             }
-        } else if (resultCode == RESULT_CANCELED) {
-            mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
-                    ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
         }
         mDragLayer.clearAnimatedView();
 
@@ -889,8 +893,7 @@
      * @return the new screen, or screenId if it exists
      */
     private long ensurePendingDropLayoutExists(long screenId) {
-        CellLayout dropLayout =
-                (CellLayout) mWorkspace.getScreenWithId(screenId);
+        CellLayout dropLayout = mWorkspace.getScreenWithId(screenId);
         if (dropLayout == null) {
             // it's possible that the add screen was removed because it was
             // empty and a re-bind occurred
@@ -968,6 +971,7 @@
         }
 
         super.onResume();
+        getUserEventDispatcher().resetElapsedSessionMillis();
 
         // Restore the previous launcher state
         if (mOnResumeState == State.WORKSPACE) {
@@ -991,13 +995,7 @@
         mPaused = false;
         if (mRestoring || 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);
+            mModel.startLoader(getCurrentWorkspaceScreen());
             mRestoring = false;
             mOnResumeNeedsLoad = false;
         }
@@ -1125,18 +1123,13 @@
          * Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost
          * screen (or in the case of RTL, the rightmost screen).
          */
-        public void onScrollChange(int progress, boolean rtl);
+        public void onScrollChange(float progress, boolean rtl);
 
         /**
-         * Screen has stopped scrolling
+         * Called when the launcher is ready to use the overlay
+         * @param callbacks A set of callbacks provided by Launcher in relation to the overlay
          */
-        public void onScrollSettled();
-
-        /**
-         * This method can be called by the Launcher in order to force the LauncherOverlay
-         * to exit fully immersive mode.
-         */
-        public void forceExitFullImmersion();
+        public void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
     }
 
     public interface LauncherSearchCallbacks {
@@ -1152,50 +1145,15 @@
     }
 
     public interface LauncherOverlayCallbacks {
-        /**
-         * This method indicates whether a call to {@link #enterFullImmersion()} will succeed,
-         * however it doesn't modify any state within the launcher.
-         */
-        public boolean canEnterFullImmersion();
-
-        /**
-         * Should be called to tell Launcher that the LauncherOverlay will take over interaction,
-         * eg. by occupying the full screen and handling all touch events.
-         *
-         * @return true if Launcher allows the LauncherOverlay to become fully immersive. In this
-         *          case, Launcher will modify any necessary state and assumes the overlay is
-         *          handling all interaction. If false, the LauncherOverlay should cancel any
-         *
-         */
-        public boolean enterFullImmersion();
-
-        /**
-         * Must be called when exiting fully immersive mode. Indicates to Launcher that it has
-         * full control over UI and state.
-         */
-        public void exitFullImmersion();
+        public void onScrollChanged(float progress);
     }
 
     class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
 
-        @Override
-        public boolean canEnterFullImmersion() {
-            return mState == State.WORKSPACE;
-        }
-
-        @Override
-        public boolean enterFullImmersion() {
-            if (mState == State.WORKSPACE) {
-                // When fully immersed, disregard any touches which fall through.
-                mDragLayer.setBlockTouch(true);
-                return true;
+        public void onScrollChanged(float progress) {
+            if (mWorkspace != null) {
+                mWorkspace.onOverlayScrollChanged(progress);
             }
-            return false;
-        }
-
-        @Override
-        public void exitFullImmersion() {
-            mDragLayer.setBlockTouch(false);
         }
     }
 
@@ -1205,7 +1163,7 @@
         } else {
             // On devices with a locked orientation, we will at least have the allow rotation
             // setting.
-            return !Utilities.isRotationAllowedForDevice(this);
+            return !getResources().getBoolean(R.bool.allow_rotation);
         }
     }
 
@@ -1344,16 +1302,9 @@
             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);
+        ContentValues itemValues = savedState.getParcelable(RUNTIME_STATE_PENDING_ADD_ITEM);
+        if (itemValues != null) {
+            mPendingAddInfo.readFromValues(itemValues);
             AppWidgetProviderInfo info = savedState.getParcelable(
                     RUNTIME_STATE_PENDING_ADD_WIDGET_INFO);
             mPendingAddWidgetInfo = info == null ?
@@ -1375,11 +1326,11 @@
         mFocusHandler = (FocusIndicatorView) findViewById(R.id.focus_indicator);
         mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
         mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
-        mWorkspace.setPageSwitchListener(this);
         mPageIndicators = mDragLayer.findViewById(R.id.page_indicator);
 
-        mLauncherView.setSystemUiVisibility(
-                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
+        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
@@ -1400,9 +1351,12 @@
         mWorkspace.setup(dragController);
         dragController.addDragListener(mWorkspace);
 
-        // Get the search/delete bar
+        // Get the search/delete/uninstall bar
         mSearchDropTargetBar = (SearchDropTargetBar)
                 mDragLayer.findViewById(R.id.search_drop_target_bar);
+        // Get the app info bar
+        mAppInfoDropTargetBar = (AppInfoDropTargetBar)
+                mDragLayer.findViewById(R.id.app_info_drop_target_bar);
 
         // Setup Apps and Widgets
         mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
@@ -1422,6 +1376,9 @@
             mSearchDropTargetBar.setup(this, dragController);
             mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());
         }
+        if (mAppInfoDropTargetBar != null) {
+            mAppInfoDropTargetBar.setup(this, dragController);
+        }
 
         if (TestingUtils.MEMORY_DUMP_ENABLED) {
             TestingUtils.addWeightWatcher(this);
@@ -1519,7 +1476,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);
@@ -1536,7 +1493,6 @@
     private void completeAddShortcut(Intent data, long container, long screenId, int cellX,
             int cellY) {
         int[] cellXY = mTmpAddItemCellCoordinates;
-        int[] touchXY = mPendingAddInfo.dropPos;
         CellLayout layout = getCellLayout(container, screenId);
 
         ShortcutInfo info = InstallShortcutReceiver.fromShortcutIntent(this, data);
@@ -1563,10 +1519,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);
         }
@@ -1594,8 +1546,7 @@
 
         ItemInfo info = mPendingAddInfo;
         if (appWidgetInfo == null) {
-            appWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(this,
-                    mAppWidgetManager.getAppWidgetInfo(appWidgetId));
+            appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId);
         }
 
         if (appWidgetInfo.isCustomWidget) {
@@ -1695,6 +1646,10 @@
         FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
         mAttached = true;
         mVisible = true;
+
+        if (mLauncherCallbacks != null) {
+            mLauncherCallbacks.onAttachedToWindow();
+        }
     }
 
     @Override
@@ -1707,6 +1662,10 @@
             mAttached = false;
         }
         updateAutoAdvanceState();
+
+        if (mLauncherCallbacks != null) {
+            mLauncherCallbacks.onDetachedFromWindow();
+        }
     }
 
     public void onWindowVisibilityChanged(int visibility) {
@@ -1762,11 +1721,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);
@@ -1783,7 +1742,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() {
@@ -1793,7 +1752,7 @@
                     }
                     i++;
                 }
-                sendAdvanceMessage(mAdvanceInterval);
+                sendAdvanceMessage(ADVANCE_INTERVAL);
             }
             return true;
         }
@@ -1849,6 +1808,10 @@
         return mSearchDropTargetBar;
     }
 
+    public AppInfoDropTargetBar getAppInfoDropTargetBar() {
+        return mAppInfoDropTargetBar;
+    }
+
     public LauncherAppWidgetHost getAppWidgetHost() {
         return mAppWidgetHost;
     }
@@ -1857,7 +1820,7 @@
         return mModel;
     }
 
-    protected SharedPreferences getSharedPrefs() {
+    public SharedPreferences getSharedPrefs() {
         return mSharedPrefs;
     }
 
@@ -1981,23 +1944,18 @@
         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);
 
         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);
+            ContentValues itemValues = new ContentValues();
+            mPendingAddInfo.writeToValues(itemValues);
+            outState.putParcelable(RUNTIME_STATE_PENDING_ADD_ITEM, itemValues);
             outState.putParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO, mPendingAddWidgetInfo);
             outState.putInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID, mPendingAddWidgetId);
         }
 
-        // Save the current widgets tray?
-        // TODO(hyunyoungs)
-
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onSaveInstanceState(outState);
         }
@@ -2013,13 +1971,15 @@
         mWorkspace.removeCallbacks(mBuildLayersRunnable);
 
         // 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 {
@@ -2033,13 +1993,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(mCloseSystemDialogsReceiver);
 
         LauncherAnimUtils.onDestroyActivity();
 
@@ -2222,7 +2179,6 @@
         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
@@ -2297,7 +2253,6 @@
         resetAddInfo();
         mPendingAddInfo.container = container;
         mPendingAddInfo.screenId = screenId;
-        mPendingAddInfo.dropPos = null;
 
         if (cell != null) {
             mPendingAddInfo.cellX = cell[0];
@@ -2321,7 +2276,6 @@
         resetAddInfo();
         mPendingAddInfo.container = info.container = container;
         mPendingAddInfo.screenId = info.screenId = screenId;
-        mPendingAddInfo.dropPos = null;
         mPendingAddInfo.minSpanX = info.minSpanX;
         mPendingAddInfo.minSpanY = info.minSpanY;
 
@@ -2380,7 +2334,6 @@
         // Update the model
         LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId,
                 cellX, cellY);
-        sFolders.put(folderInfo.id, folderInfo);
 
         // Create the view
         FolderIcon newFolder =
@@ -2403,9 +2356,9 @@
     public boolean removeItem(View v, 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);
             }
@@ -2414,7 +2367,6 @@
             }
         } else if (itemInfo instanceof FolderInfo) {
             final FolderInfo folderInfo = (FolderInfo) itemInfo;
-            unbindFolder(folderInfo);
             mWorkspace.removeWorkspaceItem(v);
             if (deleteFromDb) {
                 LauncherModel.deleteFolderAndContentsFromDatabase(this, folderInfo);
@@ -2435,13 +2387,6 @@
     }
 
     /**
-     * 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) {
@@ -2552,8 +2497,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();
@@ -2591,10 +2538,10 @@
         final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
         if (v.isReadyForClickSetup()) {
             int widgetId = info.appWidgetId;
-            AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
+            LauncherAppWidgetProviderInfo appWidgetInfo =
+                    mAppWidgetManager.getLauncherAppWidgetInfo(widgetId);
             if (appWidgetInfo != null) {
-                mPendingAddWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(
-                        this, appWidgetInfo);
+                mPendingAddWidgetInfo = appWidgetInfo;
                 mPendingAddInfo.copyFrom(info);
                 mPendingAddWidgetId = widgetId;
 
@@ -2628,10 +2575,6 @@
         if (!isAppsViewVisible()) {
             showAppsView(true /* animated */, false /* resetListToTop */,
                     true /* updatePredictedApps */, false /* focusSearchBar */);
-
-            if (mLauncherCallbacks != null) {
-                mLauncherCallbacks.onClickAllAppsButton(v);
-            }
         }
     }
 
@@ -2676,12 +2619,17 @@
         final ShortcutInfo shortcut = (ShortcutInfo) tag;
 
         if (shortcut.isDisabled != 0) {
-            int error = R.string.activity_not_available;
-            if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SAFEMODE) != 0) {
-                error = R.string.safemode_shortcut_error;
+            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.
+            } else {
+                int error = R.string.activity_not_available;
+                if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SAFEMODE) != 0) {
+                    error = R.string.safemode_shortcut_error;
+                }
+                Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
+                return;
             }
-            Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
-            return;
         }
 
         // Check for abandoned promise
@@ -2700,10 +2648,6 @@
 
         // Start activities
         startAppShortcutOrInfoActivity(v);
-
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onClickAppShortcut(v);
-        }
     }
 
     @Thunk void startAppShortcutOrInfoActivity(View v) {
@@ -2726,7 +2670,7 @@
         }
 
         boolean success = startActivitySafely(v, intent, tag);
-        mStats.recordLaunch(v, intent, shortcut);
+        getUserEventDispatcher().logAppLaunch(v, intent);
 
         if (success && v instanceof BubbleTextView) {
             mWaitingForResume = (BubbleTextView) v;
@@ -2745,42 +2689,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);
         }
     }
 
@@ -2794,9 +2706,6 @@
             Toast.makeText(this, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
         } else {
             showWidgetsView(true /* animated */, true /* resetPageToZero */);
-            if (mLauncherCallbacks != null) {
-                mLauncherCallbacks.onClickAddWidgetButton(view);
-            }
         }
     }
 
@@ -2805,29 +2714,25 @@
      * on the home screen.
      */
     protected 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");
         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);
-        }
+        // TODO: Start the system wallpaper picker
     }
 
     /**
      * Event handler for a click on the settings button that appears after a long press
      * on the home screen.
      */
-    protected void onClickSettingsButton(View v) {
+    private void onClickSettingsButton(View v) {
         if (LOGD) Log.d(TAG, "onClickSettingsButton");
-        if (mLauncherCallbacks != null) {
-            mLauncherCallbacks.onClickSettingsButton(v);
-        } else {
-            startActivity(new Intent(this, SettingsActivity.class));
-        }
+        startActivity(new Intent(Utilities.ACTION_APPLICATION_PREFERENCES)
+                .setPackage(getPackageName()));
     }
 
     public View.OnTouchListener getHapticFeedbackTouchListener() {
@@ -2846,16 +2751,17 @@
         return mHapticFeedbackTouchListener;
     }
 
+    @Override
+    public void onAccessibilityStateChanged(boolean enabled) {
+        mDragLayer.onAccessibilityStateChanged(enabled);
+    }
+
     public void onDragStarted(View view) {
         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);
-        }
     }
 
     /**
@@ -2896,43 +2802,6 @@
         }
     }
 
-    void startApplicationDetailsActivity(ComponentName componentName, UserHandleCompat user) {
-        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");
-        }
-    }
-
-    // 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 {
@@ -3085,10 +2954,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();
@@ -3100,38 +2965,33 @@
         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));
         }
         oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
         oa.start();
-        if (Utilities.isPowerSaverOn(this)) {
-            // Animations are disabled in battery saver mode, so just skip to the end state.
-            oa.end();
-        }
     }
 
     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);
@@ -3149,9 +3009,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) {
@@ -3170,12 +3031,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
@@ -3199,6 +3061,8 @@
     }
 
     public void closeFolder(Folder folder, boolean animate) {
+        animate &= !Utilities.isPowerSaverOn(this);
+
         folder.getInfo().opened = false;
 
         ViewGroup parent = (ViewGroup) folder.getParent().getParent();
@@ -3220,6 +3084,7 @@
         getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
     }
 
+    @Override
     public boolean onLongClick(View v) {
         if (!isDraggingEnabled()) return false;
         if (isWorkspaceLocked()) return false;
@@ -3355,38 +3220,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) {
             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) {
@@ -3435,9 +3279,7 @@
         }
         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;
     }
 
@@ -3514,31 +3356,50 @@
      * 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 onLauncherClingShown() {
+        // When a launcher cling appears, it should cover the underlying layers, so their focus
+        // should be blocked.
+        if (mDragLayer.getDescendantFocusability() != ViewGroup.FOCUS_BLOCK_DESCENDANTS) {
+            mDragLayer.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+        }
+    }
+
+    public void onLauncherClingDismissed() {
+        mDragLayer.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
+    }
+
     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() {
             @Override
@@ -3558,12 +3419,19 @@
         }, 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 */,
                     false /* updatePredictedApps */, false /* focusSearchBar */);
         } else if (mState == State.WIDGETS_SPRING_LOADED) {
             showWidgetsView(true, false);
+        } else if (mState == State.WORKSPACE_SPRING_LOADED) {
+            showWorkspace(true);
         }
     }
 
@@ -3576,6 +3444,7 @@
             List<ComponentKey> apps = mLauncherCallbacks.getPredictedApps();
             if (apps != null) {
                 mAppsView.setPredictedApps(apps);
+                getUserEventDispatcher().setPredictedApps(apps);
             }
         }
     }
@@ -3588,10 +3457,6 @@
         // TODO
     }
 
-    protected void disableVoiceButtonProxy(boolean disable) {
-        // NO-OP
-    }
-
     public boolean launcherCallbacksProvidesSearch() {
         return (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch());
     }
@@ -3767,6 +3632,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");
@@ -3780,11 +3646,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;
         }
     }
 
@@ -3796,11 +3676,6 @@
     public void startBinding() {
         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();
@@ -3922,7 +3797,7 @@
                             Object tag = v.getTag();
                             String desc = "Collision while binding workspace item: " + item
                                     + ". Collides with " + tag;
-                            if (LauncherAppState.isDogfoodBuild()) {
+                            if (ProviderConfig.IS_DOGFOOD_BUILD) {
                                 throw (new RuntimeException(desc));
                             } else {
                                 Log.d(TAG, desc);
@@ -3982,19 +3857,14 @@
         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);
+        mWorkspace.requestLayout();
     }
 
     /**
@@ -4012,19 +3882,31 @@
             return;
         }
 
+        if (mIsSafeModeEnabled) {
+            bindSafeModeWidget(item);
+            return;
+        }
+
         final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
         if (DEBUG_WIDGETS) {
             Log.d(TAG, "bindAppWidget: " + item);
         }
-        final Workspace workspace = mWorkspace;
 
-        LauncherAppWidgetProviderInfo appWidgetInfo =
-                LauncherModel.getProviderInfo(this, item.providerName, item.user);
+        final LauncherAppWidgetProviderInfo appWidgetInfo;
 
-        if (!mIsSafeModeEnabled
-                && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0)
-                && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
+        if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
+            // If the provider is not ready, bind as a pending widget.
+            appWidgetInfo = null;
+        } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+            // The widget id is not valid. Try to find the widget based on the provider info.
+            appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user);
+        } else {
+            appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId);
+        }
 
+        // If the provider is ready, but the width is not yet restored, try to restore it.
+        if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) &&
+                (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
             if (appWidgetInfo == null) {
                 if (DEBUG_WIDGETS) {
                     Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
@@ -4036,11 +3918,11 @@
             }
 
             // If we do not have a valid id, try to bind an id.
-            if ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0) {
+            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);
+                PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(this, appWidgetInfo);
                 pendingInfo.spanX = item.spanX;
                 pendingInfo.spanY = item.spanY;
                 pendingInfo.minSpanX = item.minSpanX;
@@ -4072,46 +3954,42 @@
                         : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
 
                 LauncherModel.updateItemInDatabase(this, item);
-            } else if (((item.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0)
+            } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
                     && (appWidgetInfo.configure == null)) {
-                // If the ID is already valid, verify if we need to configure or not.
+                // The widget was marked as UI not ready, but there is no configure activity to
+                // update the UI.
                 item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
                 LauncherModel.updateItemInDatabase(this, item);
             }
         }
 
-        if (!mIsSafeModeEnabled && item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
-            final int appWidgetId = item.appWidgetId;
+        if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
             if (DEBUG_WIDGETS) {
                 Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component "
                         + appWidgetInfo.provider);
             }
 
             // Verify that we own the widget
-            AppWidgetProviderInfo info = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
-            if (info == null || appWidgetInfo == null ||
-                    !info.provider.equals(appWidgetInfo.provider)) {
-                Log.e(TAG, "Removing invalid widget: id=" + item.appWidgetId + " info=" + info
-                        + " appWidgetInfo=" + appWidgetInfo);
+            if (appWidgetInfo == null) {
+                FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
                 deleteWidgetInfo(item);
                 return;
             }
 
-            item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+            item.hostView = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
             item.minSpanX = appWidgetInfo.minSpanX;
             item.minSpanY = appWidgetInfo.minSpanY;
+            addAppWidgetToWorkspace(item, appWidgetInfo, false);
         } else {
-            appWidgetInfo = null;
             PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item,
                     mIsSafeModeEnabled);
             view.updateIcon(mIconCache);
             item.hostView = view;
             item.hostView.updateAppWidget(null);
             item.hostView.setOnClickListener(this);
+            addAppWidgetToWorkspace(item, null, false);
         }
-
-        addAppWidgetToWorkspace(item, appWidgetInfo, false);
-        workspace.requestLayout();
+        mWorkspace.requestLayout();
 
         if (DEBUG_WIDGETS) {
             Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
@@ -4123,7 +4001,6 @@
      * 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) {
         LauncherAppWidgetHostView view = mWorkspace.getWidgetForAppWidgetId(appWidgetId);
@@ -4143,6 +4020,21 @@
         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;
+        }
+    }
+
     /**
      * Callback saying that there aren't any more items to bind.
      *
@@ -4161,13 +4053,13 @@
             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.
@@ -4193,18 +4085,6 @@
         }
     }
 
-    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();
-        }
-    }
-
     public boolean isAllAppsButtonRank(int rank) {
         if (mHotseat != null) {
             return mHotseat.isAllAppsButtonRank(rank);
@@ -4219,10 +4099,7 @@
     }
 
     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));
@@ -4348,7 +4225,7 @@
             }
             mWorkspace.removeItemsByComponentName(removedComponents, user);
             // Notify the drag controller
-            mDragController.onAppsRemoved(new ArrayList<String>(), removedComponents);
+            mDragController.onAppsRemoved(new HashSet<String>(), removedComponents);
         }
     }
 
@@ -4372,43 +4249,44 @@
     }
 
     /**
-     * A package was uninstalled.  We take both the super set of packageNames
+     * A package was uninstalled/updated.  We take both the super set of packageNames
      * in addition to specific applications to remove, the reason being that
      * this can be called when a package is updated as well.  In that scenario,
-     * we only remove specific components from the workspace, where as
+     * we only remove specific components from the workspace and hotseat, where as
      * package-removal should clear all items by package name.
-     *
-     * @param reason if non-zero, the icons are not permanently removed, rather marked as disabled.
-     * Implementation of the method from LauncherModel.Callbacks.
      */
     @Override
-    public void bindComponentsRemoved(final ArrayList<String> packageNames,
-            final ArrayList<AppInfo> appInfos, final UserHandleCompat user, final int reason) {
+    public void bindWorkspaceComponentsRemoved(
+            final HashSet<String> packageNames, final HashSet<ComponentName> components,
+            final UserHandleCompat user) {
         Runnable r = new Runnable() {
             public void run() {
-                bindComponentsRemoved(packageNames, appInfos, user, reason);
+                bindWorkspaceComponentsRemoved(packageNames, components, user);
             }
         };
         if (waitUntilResume(r)) {
             return;
         }
+        if (!packageNames.isEmpty()) {
+            mWorkspace.removeItemsByPackageName(packageNames, user);
+        }
+        if (!components.isEmpty()) {
+            mWorkspace.removeItemsByComponentName(components, user);
+        }
+        // Notify the drag controller
+        mDragController.onAppsRemoved(packageNames, components);
 
-        if (reason == 0) {
-            HashSet<ComponentName> removedComponents = new HashSet<ComponentName>();
-            for (AppInfo info : appInfos) {
-                removedComponents.add(info.componentName);
-            }
-            if (!packageNames.isEmpty()) {
-                mWorkspace.removeItemsByPackageName(packageNames, user);
-            }
-            if (!removedComponents.isEmpty()) {
-                mWorkspace.removeItemsByComponentName(removedComponents, user);
-            }
-            // Notify the drag controller
-            mDragController.onAppsRemoved(packageNames, removedComponents);
+    }
 
-        } else {
-            mWorkspace.disableShortcutsByPackageName(packageNames, user, reason);
+    @Override
+    public void bindAppInfosRemoved(final ArrayList<AppInfo> appInfos) {
+        Runnable r = new Runnable() {
+            public void run() {
+                bindAppInfosRemoved(appInfos);
+            }
+        };
+        if (waitUntilResume(r)) {
+            return;
         }
 
         // Update AllApps
@@ -4417,15 +4295,15 @@
         }
     }
 
-    private Runnable mBindPackagesUpdatedRunnable = new Runnable() {
+    private Runnable mBindWidgetModelRunnable = new Runnable() {
             public void run() {
-                bindAllPackages(mWidgetsModel);
+                bindWidgetsModel(mWidgetsModel);
             }
         };
 
     @Override
-    public void bindAllPackages(final WidgetsModel model) {
-        if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
+    public void bindWidgetsModel(WidgetsModel model) {
+        if (waitUntilResume(mBindWidgetModelRunnable, true)) {
             mWidgetsModel = model;
             return;
         }
@@ -4436,6 +4314,13 @@
         }
     }
 
+    @Override
+    public void notifyWidgetProvidersChanged() {
+        if (mWorkspace.getState().shouldUpdateWidget) {
+            mModel.refreshAndBindWidgetsAndShortcuts(this, mWidgetsView.isEmpty());
+        }
+    }
+
     private int mapConfigurationOriActivityInfoOri(int configOri) {
         final Display d = getWindowManager().getDefaultDisplay();
         int naturalOri = Configuration.ORIENTATION_LANDSCAPE;
@@ -4489,15 +4374,12 @@
                     public void run() {
                         setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
                     }
-                }, mRestoreScreenOrientationDelay);
+                }, RESTORE_SCREEN_ORIENTATION_DELAY);
             }
         }
     }
 
     protected boolean isLauncherPreinstalled() {
-        if (mLauncherCallbacks != null) {
-            return mLauncherCallbacks.isLauncherPreinstalled();
-        }
         PackageManager pm = getPackageManager();
         try {
             ApplicationInfo ai = pm.getApplicationInfo(getComponentName().getPackageName(), 0);
@@ -4513,17 +4395,6 @@
     }
 
     /**
-     * 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();
-        }
-        return true;
-    }
-
-    /**
      * To be overridden by subclasses to indicate that there is an activity to launch
      * before showing the standard launcher experience.
      */
@@ -4608,9 +4479,6 @@
         if (introScreen != null) {
             mDragLayer.showOverlayView(introScreen);
         }
-        if (mLauncherOverlayContainer != null) {
-            mLauncherOverlayContainer.setVisibility(View.INVISIBLE);
-        }
     }
 
     public void dismissIntroScreen() {
@@ -4622,17 +4490,11 @@
                 @Override
                 public void run() {
                     mDragLayer.dismissOverlayView();
-                    if (mLauncherOverlayContainer != null) {
-                        mLauncherOverlayContainer.setVisibility(View.VISIBLE);
-                    }
                     showFirstRunClings();
                 }
             }, ACTIVITY_START_DELAY);
         } else {
             mDragLayer.dismissOverlayView();
-            if (mLauncherOverlayContainer != null) {
-                mLauncherOverlayContainer.setVisibility(View.VISIBLE);
-            }
             showFirstRunClings();
         }
         changeWallpaperVisiblity(true);
@@ -4723,13 +4585,6 @@
         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.
      */
@@ -4757,8 +4612,6 @@
         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());
         mModel.dumpState();
         // TODO(hyunyoungs): add mWidgetsView.dumpState(); or mWidgetsModel.dumpState();
 
@@ -4768,54 +4621,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);
     }
@@ -4824,53 +4663,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();
@@ -4878,6 +4670,25 @@
             return Collections.EMPTY_LIST;
         }
     }
+
+    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();
+        }
+    }
 }
 
 interface DebugIntents {
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 5271029..9d889e0 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -17,6 +17,7 @@
 package com.android.launcher3;
 
 import android.app.SearchManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -26,8 +27,9 @@
 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.dynamicui.ExtractionUtils;
 import com.android.launcher3.util.ConfigMonitor;
+import com.android.launcher3.logging.FileLog;
 import com.android.launcher3.util.TestingUtils;
 import com.android.launcher3.util.Thunk;
 
@@ -40,7 +42,7 @@
     private final IconCache mIconCache;
     private final WidgetPreviewLoader mWidgetCache;
 
-    private boolean mWallpaperChangedSinceLastCheck;
+    @Thunk boolean mWallpaperChangedSinceLastCheck;
 
     private static WeakReference<LauncherProvider> sLauncherProvider;
     private static Context sContext;
@@ -66,11 +68,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() {
@@ -100,13 +109,28 @@
         // 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);
+        // 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);
     }
 
     /**
@@ -129,7 +153,7 @@
     }
 
     LauncherModel setLauncher(Launcher launcher) {
-        getLauncherProvider().setLauncherProviderChangeListener(launcher);
+        sLauncherProvider.get().setLauncherProviderChangeListener(launcher);
         mModel.initialize(launcher);
         mAccessibilityDelegate = ((launcher != null) && Utilities.ATLEAST_LOLLIPOP) ?
             new LauncherAccessibilityDelegate(launcher) : null;
@@ -148,22 +172,10 @@
         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 boolean hasWallpaperChangedSinceLastCheck() {
         boolean result = mWallpaperChangedSinceLastCheck;
         mWallpaperChangedSinceLastCheck = false;
@@ -173,8 +185,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 b07ccc3..8c23ff3 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -102,6 +102,10 @@
                 callback.run();
             }
         }
+
+        if (Utilities.ATLEAST_MARSHMALLOW) {
+            mLauncher.notifyWidgetProvidersChanged();
+        }
     }
 
     public AppWidgetHostView createView(Context context, int appWidgetId,
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 4185257..28557d0 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -25,10 +25,13 @@
 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;
+import com.android.launcher3.dragndrop.DragLayer.TouchCompleteListener;
 
 import java.util.ArrayList;
 
@@ -42,18 +45,20 @@
     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;
 
     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());
@@ -101,7 +106,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;
         }
@@ -251,7 +256,7 @@
     @Override
     public void requestChildFocus(View child, View focused) {
         super.requestChildFocus(child, focused);
-        dispatchChildFocus(focused != null);
+        dispatchChildFocus(mChildrenFocused && focused != null);
         if (focused != null) {
             focused.setFocusableInTouchMode(false);
         }
@@ -287,4 +292,10 @@
             });
         }
     }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setClassName(getClass().getName());
+    }
 }
diff --git a/src/com/android/launcher3/LauncherBackupAgent.java b/src/com/android/launcher3/LauncherBackupAgent.java
new file mode 100644
index 0000000..b2f5c57
--- /dev/null
+++ b/src/com/android/launcher3/LauncherBackupAgent.java
@@ -0,0 +1,133 @@
+package com.android.launcher3;
+
+import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.ParcelFileDescriptor;
+
+import com.android.launcher3.LauncherProvider.DatabaseHelper;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.logging.FileLog;
+
+import java.io.InvalidObjectException;
+
+public class LauncherBackupAgent extends BackupAgent {
+
+    private static final String TAG = "LauncherBackupAgent";
+
+    private static final String INFO_COLUMN_NAME = "name";
+    private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
+
+    @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() {
+        DatabaseHelper helper = new DatabaseHelper(this, null, LauncherFiles.LAUNCHER_DB);
+
+        if (!sanitizeDBSafely(helper)) {
+            helper.createEmptyDB(helper.getWritableDatabase());
+        }
+
+        try {
+            // Flush all logs before the process is killed.
+            FileLog.flushAll(null);
+        } catch (Exception e) { }
+    }
+
+    private boolean sanitizeDBSafely(DatabaseHelper helper) {
+        SQLiteDatabase db = helper.getWritableDatabase();
+        db.beginTransaction();
+        try {
+            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.
+        ContentValues values = new ContentValues();
+        values.put(Favorites.RESTORED, 1);
+        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);
+        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 for 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");
+        }
+    }
+}
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
index 2177f52..c2ab20a 100644
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -25,7 +25,7 @@
 import android.os.ParcelFileDescriptor;
 import android.util.Log;
 
-import com.android.launcher3.model.MigrateFromRestoreTask;
+import com.android.launcher3.model.GridSizeMigrationTask;
 
 import java.io.IOException;
 
@@ -93,7 +93,8 @@
         }
 
         // Clear dB before restore
-        LauncherAppState.getLauncherProvider().createEmptyDB();
+        LauncherSettings.Settings.call(getContentResolver(),
+                LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
 
         boolean hasData;
         try {
@@ -110,25 +111,27 @@
         }
 
         if (hasData && mHelper.restoreSuccessful) {
-            LauncherAppState.getLauncherProvider().clearFlagEmptyDbCreated();
+            LauncherSettings.Settings.call(getContentResolver(),
+                    LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG);
             LauncherClings.markFirstRunClingDismissed(this);
 
             // Rank was added in v4.
             if (mHelper.restoredBackupVersion <= 3) {
-                LauncherAppState.getLauncherProvider().updateFolderItemsRank();
+                LauncherSettings.Settings.call(getContentResolver(),
+                        LauncherSettings.Settings.METHOD_UPDATE_FOLDER_ITEMS_RANK);
             }
 
-            if (MigrateFromRestoreTask.ENABLED && mHelper.shouldAttemptWorkspaceMigration()) {
-                MigrateFromRestoreTask.markForMigration(getApplicationContext(),
-                        (int) mHelper.migrationCompatibleProfileData.desktopCols,
-                        (int) mHelper.migrationCompatibleProfileData.desktopRows,
-                        mHelper.widgetSizes);
+            if (GridSizeMigrationTask.ENABLED && mHelper.shouldAttemptWorkspaceMigration()) {
+                GridSizeMigrationTask.markForMigration(getApplicationContext(),
+                        mHelper.widgetSizes, mHelper.migrationCompatibleProfileData);
             }
 
-            LauncherAppState.getLauncherProvider().convertShortcutsToLauncherActivities();
+            LauncherSettings.Settings.call(getContentResolver(),
+                    LauncherSettings.Settings.METHOD_CONVERT_SHORTCUTS_TO_ACTIVITIES);
         } else {
             if (VERBOSE) Log.v(TAG, "Nothing was restored, clearing DB");
-            LauncherAppState.getLauncherProvider().createEmptyDB();
+            LauncherSettings.Settings.call(getContentResolver(),
+                    LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
         }
     }
 }
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 47d1ce9..7238f70 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -50,9 +50,10 @@
 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.MigrateFromRestoreTask;
+import com.android.launcher3.model.GridSizeMigrationTask;
 import com.android.launcher3.util.Thunk;
 import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
 import com.google.protobuf.nano.MessageNano;
@@ -102,14 +103,13 @@
         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
+        Favorites.ITEM_TYPE,               // 11
+        Favorites.SCREEN,                  // 12
+        Favorites.SPANX,                   // 13
+        Favorites.SPANY,                   // 14
+        Favorites.TITLE,                   // 15
+        Favorites.PROFILE_ID,              // 16
+        Favorites.RANK,                    // 17
     };
 
     private static final int ID_INDEX = 0;
@@ -123,13 +123,12 @@
     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 int ITEM_TYPE_INDEX = 11;
+    private static final int SCREEN_INDEX = 12;
+    private static final int SPANX_INDEX = 13;
+    private static final int SPANY_INDEX = 14;
+    private static final int TITLE_INDEX = 15;
+    private static final int RANK_INDEX = 17;
 
     private static final String[] SCREEN_PROJECTION = {
         WorkspaceScreens._ID,              // 0
@@ -315,14 +314,13 @@
             return true;
         }
 
-        if (MigrateFromRestoreTask.ENABLED &&
-                (oldProfile.desktopCols - currentProfile.desktopCols <= 1) &&
-                (oldProfile.desktopRows - currentProfile.desktopRows <= 1)) {
-            // Allow desktop migration when row and/or column count contracts by 1.
-
+        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;
@@ -661,12 +659,14 @@
                 + 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) {
@@ -685,11 +685,14 @@
                 } else if (backupKey != null) {
                     if (DEBUG) Log.d(TAG, "I can count this high: " + backupWidgetCount);
                     if (backupWidgetCount < MAX_WIDGETS_PER_PASS) {
-                        if (DEBUG) Log.d(TAG, "saving widget " + backupKey);
-                        UserHandleCompat user = UserHandleCompat.myUserHandle();
-                        writeRowToBackup(key, packWidget(dpi, provider, user), data);
-                        mKeys.add(key);
-                        backupWidgetCount ++;
+                        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.
@@ -809,7 +812,6 @@
         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);
@@ -835,15 +837,11 @@
                 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;
-                }
+            String iconPackage = c.getString(ICON_PACKAGE_INDEX);
+            String iconResource = c.getString(ICON_RESOURCE_INDEX);
+            if (!TextUtils.isEmpty(iconPackage) && !TextUtils.isEmpty(iconResource)) {
+                favorite.iconResource = iconResource;
+                favorite.iconPackage = iconPackage;
             }
 
             byte[] blob = c.getBlob(ICON_INDEX);
@@ -901,11 +899,8 @@
         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_PACKAGE, favorite.iconPackage);
+            values.put(Favorites.ICON_RESOURCE, favorite.iconResource);
             values.put(Favorites.ICON, favorite.icon);
         }
 
@@ -1005,16 +1000,14 @@
     }
 
     /** Serialize a widget for persistence, including a checksum wrapper. */
-    private Widget packWidget(int dpi, ComponentName provider, UserHandleCompat user) {
-        final LauncherAppWidgetProviderInfo info =
-                LauncherModel.getProviderInfo(mContext, provider, user);
+    private Widget packWidget(int dpi, LauncherAppWidgetProviderInfo info) {
         Widget widget = new Widget();
-        widget.provider = provider.flattenToShortString();
+        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(provider.getPackageName(), info.icon);
+            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;
@@ -1023,7 +1016,6 @@
         Point spans = info.getMinSpans(mIdp, mContext);
         widget.minSpanX = spans.x;
         widget.minSpanY = spans.y;
-
         return widget;
     }
 
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 772fbf3..e971631 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;
@@ -43,6 +59,8 @@
     public void onRequestPermissionsResult(int requestCode, String[] permissions,
             int[] grantResults);
     public void onWindowFocusChanged(boolean hasFocus);
+    public void onAttachedToWindow();
+    public void onDetachedFromWindow();
     public boolean onPrepareOptionsMenu(Menu menu);
     public void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args);
     public void onHomeIntent();
@@ -54,33 +72,16 @@
      */
     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();
 
-    /*
-     * Extension points for replacing the search experience
-     */
     @Deprecated
-    public boolean forceDisableVoiceButtonProxy();
+    public void onWorkspaceLockedChanged();
+
     public boolean providesSearch();
     public boolean startSearch(String initialQuery, boolean selectInitialQuery,
             Bundle appSearchData, Rect sourceBounds);
-    @Deprecated
-    public boolean startSearchFromAllApps(String query);
-    @Deprecated
-    public void startVoice();
     public boolean hasCustomContentToLeft();
     public void populateCustomContentContainer();
     public View getQsbBar();
@@ -89,16 +90,13 @@
     /*
      * Extensions points for adding / replacing some other aspects of the Launcher experience.
      */
+    public UserEventDispatcher getUserEventDispatcher();
     public Intent getFirstRunActivity();
     public boolean hasFirstRunActivity();
     public boolean hasDismissableIntroScreen();
     public View getIntroScreen();
     public boolean shouldMoveToDefaultScreenOnHomeIntent();
     public boolean hasSettings();
-    @Deprecated
-    public ComponentName getWallpaperPickerComponent();
-    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;
@@ -106,24 +104,6 @@
     public int getSearchBarHeight();
 
     /**
-     * Returning true will immediately result in a call to {@link #setLauncherOverlayView(ViewGroup,
-     * com.android.launcher3.Launcher.LauncherOverlayCallbacks)}.
-     *
-     * @return true if this launcher extension will provide an overlay
-     */
-    public boolean hasLauncherOverlay();
-
-    /**
-     * Handshake to establish an overlay relationship
-     *
-     * @param container Full screen overlay ViewGroup into which custom views can be placed.
-     * @param callbacks A set of callbacks provided by Launcher in relation to the overlay
-     * @return an interface used to make requests and notify the Launcher in relation to the overlay
-     */
-    public Launcher.LauncherOverlay setLauncherOverlayView(InsettableFrameLayout container,
-            Launcher.LauncherOverlayCallbacks callbacks);
-
-    /**
      * Sets the callbacks to allow reacting the actions of search overlays of the launcher.
      *
      * @param callbacks A set of callbacks to the Launcher, is actually a LauncherSearchCallback,
diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java
index 5c1e830..9b8e894 100644
--- a/src/com/android/launcher3/LauncherClings.java
+++ b/src/com/android/launcher3/LauncherClings.java
@@ -27,9 +27,11 @@
 import android.os.Bundle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnClickListener;
+import android.view.View.OnKeyListener;
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -37,7 +39,7 @@
 
 import com.android.launcher3.util.Thunk;
 
-class LauncherClings implements OnClickListener {
+class LauncherClings implements OnClickListener, OnKeyListener {
     private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
     private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
 
@@ -83,6 +85,20 @@
         }
     }
 
+    @Override
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        if (event.isPrintingKey()) {
+            // Should ignore all printing keys, otherwise they come to the search box.
+            return true;
+        }
+        if (keyCode == KeyEvent.KEYCODE_MENU) {
+            // Menu key goes to the overview mode similar to longpress, therefore it needs to
+            // dismiss the clings.
+            dismissLongPressCling();
+        }
+        return false;
+    }
+
     /**
      * Shows the migration cling.
      *
@@ -90,6 +106,7 @@
      * package was not preinstalled and there exists a db to migrate from.
      */
     public void showMigrationCling() {
+        mLauncher.onLauncherClingShown();
         mIsVisible = true;
         mLauncher.hideWorkspaceSearchAndHotseat();
 
@@ -134,7 +151,9 @@
         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);
+        final View button = content.findViewById(R.id.cling_dismiss_longpress_info);
+        button.setOnClickListener(this);
+        button.setOnKeyListener(this);
 
         if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) {
             Drawable bg = new BorderCropDrawable(mLauncher.getResources().getDrawable(R.drawable.cling_bg),
@@ -142,6 +161,7 @@
             content.setBackground(bg);
         }
 
+        mLauncher.onLauncherClingShown();
         root.addView(cling);
 
         if (showWelcome) {
@@ -159,12 +179,12 @@
                 ObjectAnimator anim;
                 if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) {
                     content.setTranslationY(-content.getMeasuredHeight());
-                    anim = LauncherAnimUtils.ofFloat(content, "translationY", 0);
+                    anim = LauncherAnimUtils.ofFloat(content, View.TRANSLATION_Y, 0);
                 } else {
                     content.setScaleX(0);
                     content.setScaleY(0);
-                    PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1);
-                    PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1);
+                    PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 1);
+                    PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1);
                     anim = LauncherAnimUtils.ofPropertyValuesHolder(content, scaleX, scaleY);
                 }
 
@@ -178,7 +198,12 @@
     @Thunk void dismissLongPressCling() {
         Runnable dismissCb = new Runnable() {
             public void run() {
-                dismissCling(mLauncher.findViewById(R.id.longpress_cling), null,
+                Runnable cb = new Runnable() {
+                    public void run() {
+                        mLauncher.onLauncherClingDismissed();
+                    }
+                };
+                dismissCling(mLauncher.findViewById(R.id.longpress_cling), cb,
                         WORKSPACE_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
             }
         };
diff --git a/src/com/android/launcher3/LauncherExterns.java b/src/com/android/launcher3/LauncherExterns.java
new file mode 100644
index 0000000..c7a8668
--- /dev/null
+++ b/src/com/android/launcher3/LauncherExterns.java
@@ -0,0 +1,32 @@
+/*
+ * 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);
+}
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index c08cd0b..adb5031 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -34,13 +34,13 @@
             WALLPAPER_CROP_PREFERENCES_KEY + XML,
             WALLPAPER_IMAGES_DB,
             WIDGET_PREVIEWS_DB,
-            MANAGED_USER_PREFERENCES_KEY,
+            MANAGED_USER_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",
+            "launches.logTap",
+            "stats.logTap",
             "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 774cca4..eaeb1ac 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -17,7 +17,6 @@
 package com.android.launcher3;
 
 import android.app.SearchManager;
-import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -34,15 +33,12 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.net.Uri;
-import android.os.DeadObjectException;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.SystemClock;
-import android.os.TransactionTooLargeException;
 import android.provider.BaseColumns;
 import android.text.TextUtils;
 import android.util.Log;
@@ -56,20 +52,28 @@
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.model.MigrateFromRestoreTask;
+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.model.GridSizeMigrationTask;
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.CursorIconInfo;
+import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.util.PackageManagerHelper;
+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;
 import java.security.InvalidParameterException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
@@ -78,6 +82,7 @@
 import java.util.List;
 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
@@ -99,7 +104,6 @@
     private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
     private static final long INVALID_SCREEN_ID = -1L;
 
-    @Thunk final boolean mAppsCanBeOnRemoveableStorage;
     private final boolean mOldContentProviderExists;
 
     @Thunk final LauncherAppState mApp;
@@ -123,12 +127,6 @@
     @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>();
-
     /**
      * Set of runnables to be called on the background thread after the workspace binding
      * is complete.
@@ -138,11 +136,9 @@
     @Thunk WeakReference<Callbacks> mCallbacks;
 
     // < only access in worker thread >
-    AllAppsList mBgAllAppsList;
+    private final AllAppsList mBgAllAppsList;
     // Entire list of widgets.
-    WidgetsModel mBgWidgetsModel;
-    // Keep a clone of widgets that can be accessed from non-worker thread.
-    WidgetsModel mFgWidgetsModel;
+    private final WidgetsModel mBgWidgetsModel;
 
     // 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
@@ -169,12 +165,6 @@
     // sBgWorkspaceScreens is the ordered set of workspace screens.
     static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
 
-    // sBgWidgetProviders is the set of widget providers including custom internal widgets
-    public static HashMap<ComponentKey, LauncherAppWidgetProviderInfo> sBgWidgetProviders;
-
-    // sBgShortcutProviders is the set of custom shortcut providers
-    public static List<ResolveInfo> sBgShortcutProviders;
-
     // 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>>();
@@ -189,12 +179,12 @@
     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 finishBindingItems();
         public void bindAppWidget(LauncherAppWidgetInfo info);
         public void bindAllApplications(ArrayList<AppInfo> apps);
@@ -207,13 +197,16 @@
                 ArrayList<ShortcutInfo> removed, UserHandleCompat user);
         public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
         public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
-        public void bindComponentsRemoved(ArrayList<String> packageNames,
-                        ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
-        public void bindAllPackages(WidgetsModel model);
+        public void bindWorkspaceComponentsRemoved(
+                HashSet<String> packageNames, HashSet<ComponentName> components,
+                UserHandleCompat user);
+        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 interface ItemInfoFilter {
@@ -223,7 +216,6 @@
     LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
         Context context = app.getContext();
 
-        mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable();
         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.
@@ -245,7 +237,6 @@
         mApp = app;
         mBgAllAppsList = new AllAppsList(iconCache, appFilter);
         mBgWidgetsModel = new WidgetsModel(context, iconCache, appFilter);
-        mFgWidgetsModel = mBgWidgetsModel.clone();
         mIconCache = iconCache;
 
         mLauncherApps = LauncherAppsCompat.getInstance(context);
@@ -486,7 +477,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);
@@ -531,8 +524,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;
 
@@ -592,11 +584,6 @@
                     "main thread");
         }
 
-        // Clear any deferred bind runnables
-        synchronized (mDeferredBindRunnables) {
-            mDeferredBindRunnables.clear();
-        }
-
         // Remove any queued UI runnables
         mHandler.cancelAll();
         // Unbind all the workspace items
@@ -627,7 +614,7 @@
      * 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
@@ -655,12 +642,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;
                 }
@@ -813,7 +795,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>();
@@ -887,7 +869,7 @@
     }
 
     private void assertWorkspaceLoaded() {
-        if (LauncherAppState.isDogfoodBuild()) {
+        if (ProviderConfig.IS_DOGFOOD_BUILD) {
             synchronized (mLock) {
                 if (!mHasLoaderCompletedOnce ||
                         (mLoaderTask != null && mLoaderTask.mIsLoadingAndBindingWorkspace)) {
@@ -939,51 +921,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.
      */
@@ -1005,7 +942,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();
@@ -1045,15 +984,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() {
@@ -1238,20 +1168,8 @@
     @Override
     public void onPackagesAvailable(String[] packageNames, UserHandleCompat user,
             boolean replacing) {
-        if (!replacing) {
-            enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packageNames,
-                    user));
-            if (mAppsCanBeOnRemoveableStorage) {
-                // Only rebind if we support removable storage. It catches the
-                // case where
-                // apps on the external sd card need to be reloaded
-                startLoaderFromBackground();
-            }
-        } else {
-            // If we are replacing then just update the packages in the list
-            enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE,
-                    packageNames, user));
-        }
+        enqueuePackageUpdated(
+                new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, packageNames, user));
     }
 
     @Override
@@ -1264,6 +1182,20 @@
         }
     }
 
+    @Override
+    public void onPackagesSuspended(String[] packageNames, UserHandleCompat user) {
+        enqueuePackageUpdated(new PackageUpdatedTask(
+                PackageUpdatedTask.OP_SUSPEND, packageNames,
+                user));
+    }
+
+    @Override
+    public void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user) {
+        enqueuePackageUpdated(new PackageUpdatedTask(
+                PackageUpdatedTask.OP_UNSUSPEND, packageNames,
+                user));
+    }
+
     /**
      * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
      * ACTION_PACKAGE_CHANGED.
@@ -1285,6 +1217,16 @@
                 || LauncherAppsCompat.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)) {
+            UserHandleCompat user = UserHandleCompat.fromIntent(intent);
+            if (user != null) {
+                enqueuePackageUpdated(new PackageUpdatedTask(
+                        PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE,
+                        new String[0], user));
+            }
+        } else if (Intent.ACTION_WALLPAPER_CHANGED.equals(action)) {
+            ExtractionUtils.startColorExtractionServiceIfNecessary(context);
         }
     }
 
@@ -1314,17 +1256,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);
-        }
     }
 
     /**
@@ -1349,17 +1287,19 @@
         // 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);
+                mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags, synchronousBindPage);
                 if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
                         && mAllAppsLoaded && mWorkspaceLoaded && !mIsLoaderTaskRunning) {
                     mLoaderTask.runBindSynchronousPage(synchronousBindPage);
@@ -1371,21 +1311,6 @@
         }
     }
 
-    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);
-            }
-        }
-    }
-
     public void stopLoader() {
         synchronized (mLock) {
             if (mLoaderTask != null) {
@@ -1411,8 +1336,7 @@
                 try {
                     screenIds.add(sc.getLong(idIndex));
                 } catch (Exception e) {
-                    Launcher.addDumpLog(TAG, "Desktop items loading interrupted"
-                            + " - invalid screens: " + e, true);
+                    FileLog.d(TAG, "Invalid screen id", e);
                 }
             }
         } finally {
@@ -1421,10 +1345,6 @@
         return screenIds;
     }
 
-    public boolean isAllAppsLoaded() {
-        return mAllAppsLoaded;
-    }
-
     /**
      * Runnable for the thread that loads the contents of the launcher:
      *   - workspace icons
@@ -1433,14 +1353,17 @@
      */
     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 flags, int pageToBindFirst) {
             mContext = context;
             mFlags = flags;
+            mPageToBindFirst = pageToBindFirst;
         }
 
         private void loadAndBindWorkspace() {
@@ -1462,7 +1385,7 @@
             }
 
             // Bind the workspace
-            bindWorkspace(-1);
+            bindWorkspace(mPageToBindFirst);
         }
 
         private void waitForIdle() {
@@ -1728,46 +1651,35 @@
             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 (MigrateFromRestoreTask.ENABLED && MigrateFromRestoreTask.shouldRunTask(mContext)) {
-                long migrationStartTime = System.currentTimeMillis();
-                Log.v(TAG, "Starting workspace migration after restore");
-                try {
-                    MigrateFromRestoreTask task = new MigrateFromRestoreTask(mContext);
-                    // Clear the flags before starting the task, so that we do not run the task
-                    // again, in case there was an uncaught error.
-                    MigrateFromRestoreTask.clearFlags(mContext);
-                    task.execute();
-                } catch (Exception e) {
-                    Log.e(TAG, "Error during grid migration", e);
-
-                    // Clear workspace.
-                    mFlags = mFlags | LOADER_FLAG_CLEAR_WORKSPACE;
-                }
-                Log.v(TAG, "Workspace migration completed in "
-                        + (System.currentTimeMillis() - migrationStartTime));
+            if (GridSizeMigrationTask.ENABLED &&
+                    !GridSizeMigrationTask.migrateGridIfNeeded(mContext)) {
+                // Migration failed. Clear workspace.
+                mFlags = mFlags | LOADER_FLAG_CLEAR_WORKSPACE;
             }
 
             if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
-                Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
-                LauncherAppState.getLauncherProvider().deleteDatabase();
+                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();
+                Log.d(TAG, "loadWorkspace: migrating from launcher2");
+                LauncherSettings.Settings.call(contentResolver,
+                        LauncherSettings.Settings.METHOD_MIGRATE_LAUNCHER2_SHORTCUTS);
             } 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) {
@@ -1776,8 +1688,8 @@
                         .getInstance(mContext).updateAndGetActiveSessionCache();
                 sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
 
-                final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
-                final ArrayList<Long> restoredRows = new ArrayList<Long>();
+                final ArrayList<Long> itemsToRemove = new ArrayList<>();
+                final ArrayList<Long> restoredRows = new ArrayList<>();
                 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);
@@ -1786,6 +1698,7 @@
                 // Load workspace in reverse order to ensure that latest items are loaded first (and
                 // before any earlier duplicates)
                 final LongArrayMap<ItemInfo[][]> occupied = new LongArrayMap<>();
+                HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
 
                 try {
                     final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
@@ -1822,8 +1735,11 @@
                     final CursorIconInfo cursorIconInfo = new CursorIconInfo(c);
 
                     final LongSparseArray<UserHandleCompat> allUsers = new LongSparseArray<>();
+                    final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
                     for (UserHandleCompat user : mUserManager.getUserProfiles()) {
-                        allUsers.put(mUserManager.getSerialNumberForUser(user), user);
+                        long serialNo = mUserManager.getSerialNumberForUser(user);
+                        allUsers.put(serialNo, user);
+                        quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
                     }
 
                     ShortcutInfo info;
@@ -1834,6 +1750,7 @@
                     long serialNumber;
                     Intent intent;
                     UserHandleCompat user;
+                    String targetPackage;
 
                     while (!mStopped && c.moveToNext()) {
                         try {
@@ -1852,6 +1769,7 @@
                                 int promiseType = c.getInt(restoredIndex);
                                 int disabledState = 0;
                                 boolean itemReplaced = false;
+                                targetPackage = null;
                                 if (user == null) {
                                     // User has been deleted remove the item.
                                     itemsToRemove.add(id);
@@ -1865,6 +1783,9 @@
                                                 cn.getPackageName(), user);
                                         boolean validComponent = validPkg &&
                                                 launcherApps.isActivityEnabledForProfile(cn, user);
+                                        if (validPkg) {
+                                            targetPackage = cn.getPackageName();
+                                        }
 
                                         if (validComponent) {
                                             if (restored) {
@@ -1872,6 +1793,9 @@
                                                 restoredRows.add(id);
                                                 restored = false;
                                             }
+                                            if (quietMode.get(serialNumber)) {
+                                                disabledState = ShortcutInfo.FLAG_DISABLED_QUIET_USER;
+                                            }
                                         } else if (validPkg) {
                                             intent = null;
                                             if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
@@ -1890,8 +1814,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 {
@@ -1902,8 +1825,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.
@@ -1929,28 +1851,24 @@
                                                     itemReplaced = true;
 
                                                 } else if (REMOVE_UNRESTORED_ICONS) {
-                                                    Launcher.addDumpLog(TAG,
-                                                            "Unrestored package removed: " + cn, true);
+                                                    FileLog.d(TAG, "Unrestored package removed: " + cn);
                                                     itemsToRemove.add(id);
                                                     continue;
                                                 }
                                             } else if (REMOVE_UNRESTORED_ICONS) {
-                                                Launcher.addDumpLog(TAG,
-                                                        "Unrestored package removed: " + cn, true);
+                                                FileLog.d(TAG, "Unrestored package removed: " + cn);
                                                 itemsToRemove.add(id);
                                                 continue;
                                             }
-                                        } else if (launcherApps.isAppEnabled(
-                                                manager, cn.getPackageName(),
-                                                PackageManager.GET_UNINSTALLED_PACKAGES)) {
+                                        } else if (PackageManagerHelper.isAppOnSdcard(
+                                                manager, cn.getPackageName())) {
                                             // Package is present but not available.
                                             allowMissingTarget = true;
                                             disabledState = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
                                         } 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>();
@@ -1963,8 +1881,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;
                                         }
@@ -1974,8 +1891,7 @@
                                         restored = false;
                                     }
                                 } catch (URISyntaxException e) {
-                                    Launcher.addDumpLog(TAG,
-                                            "Invalid uri: " + intentDescription, true);
+                                    FileLog.d(TAG, "Invalid uri: " + intentDescription);
                                     itemsToRemove.add(id);
                                     continue;
                                 }
@@ -1985,7 +1901,7 @@
 
                                 if (itemReplaced) {
                                     if (user.equals(UserHandleCompat.myUserHandle())) {
-                                        info = getAppShortcutInfo(manager, intent, user, context, null,
+                                        info = getAppShortcutInfo(intent, user, context, null,
                                                 cursorIconInfo.iconIndex, titleIndex,
                                                 false, useLowResIcon);
                                     } else {
@@ -1995,9 +1911,6 @@
                                     }
                                 } 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);
                                         intent = getRestoredItemIntent(c, context, intent);
@@ -2008,12 +1921,17 @@
                                     }
                                 } else if (itemType ==
                                         LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-                                    info = getAppShortcutInfo(manager, intent, user, context, c,
+                                    info = getAppShortcutInfo(intent, user, context, c,
                                             cursorIconInfo.iconIndex, titleIndex,
                                             allowMissingTarget, useLowResIcon);
                                 } else {
                                     info = getShortcutInfo(c, context, titleIndex, cursorIconInfo);
 
+                                    // Shortcuts are only available on the primary profile
+                                    if (PackageManagerHelper.isAppSuspended(manager, targetPackage)) {
+                                        disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+                                    }
+
                                     // App shortcuts that used to be automatically added to Launcher
                                     // didn't always have the correct intent flags set, so do that
                                     // here
@@ -2041,7 +1959,7 @@
                                     if (info.promisedIntent != null) {
                                         info.promisedIntent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
                                     }
-                                    info.isDisabled = disabledState;
+                                    info.isDisabled |= disabledState;
                                     if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
                                         info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
                                     }
@@ -2073,7 +1991,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);
@@ -2144,19 +2062,20 @@
                                 final boolean wasProviderReady = (restoreStatus &
                                         LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
 
-                                final LauncherAppWidgetProviderInfo provider =
-                                        LauncherModel.getProviderInfo(context,
+                                if (widgetProvidersMap == null) {
+                                    widgetProvidersMap = AppWidgetManagerCompat
+                                            .getInstance(mContext).getAllProvidersMap();
+                                }
+                                final AppWidgetProviderInfo provider = widgetProvidersMap.get(
+                                        new ComponentKey(
                                                 ComponentName.unflattenFromString(savedProvider),
-                                                user);
+                                                user));
 
                                 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) {
@@ -2197,8 +2116,7 @@
                                             appWidgetInfo.restoreStatus |=
                                                     LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
                                         } else if (REMOVE_UNRESTORED_ICONS && !isSafeMode) {
-                                            Launcher.addDumpLog(TAG,
-                                                    "Unrestored widget removed: " + component, true);
+                                            FileLog.d(TAG, "Unrestored widget removed: " + component);
                                             itemsToRemove.add(id);
                                             continue;
                                         }
@@ -2250,13 +2168,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
@@ -2276,8 +2192,11 @@
                     }
 
                     // 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);
@@ -2310,7 +2229,7 @@
 
                 if (!isSdCardReady && !sPendingPackages.isEmpty()) {
                     context.registerReceiver(new AppsAvailabilityCheck(),
-                            new IntentFilter(StartupReceiver.SYSTEM_READY),
+                            new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
                             null, sWorker);
                 }
 
@@ -2432,47 +2351,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);
+                    }
                 }
             });
         }
@@ -2494,10 +2407,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();
@@ -2514,32 +2424,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
@@ -2554,11 +2439,7 @@
                         }
                     }
                 };
-                if (postOnMainThread) {
-                    deferredBindRunnables.add(r);
-                } else {
-                    runOnMainThread(r);
-                }
+                executor.execute(r);
             }
         }
 
@@ -2579,21 +2460,14 @@
             }
 
             // 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 =
@@ -2613,21 +2487,15 @@
             unbindWorkspaceItemsOnMainThread();
 
             // 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);
 
@@ -2636,6 +2504,7 @@
                 public void run() {
                     Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                     if (callbacks != null) {
+                        callbacks.clearPendingBinds();
                         callbacks.startBinding();
                     }
                 }
@@ -2644,28 +2513,18 @@
 
             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 isLoadingSynchronously, only bind the first screen, and defer binding the
+            // remaining screens after first onDraw is called. This ensures that the first screen
+            // is immediately visible (eg. during rotation)
+            // In case of !isLoadingSynchronously, bind all pages one after other.
+            final Executor deferredExecutor = isLoadingSynchronously ?
+                    new ViewOnDrawExecutor(mHandler) : mainExecutor;
+
+            bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, deferredExecutor);
 
             // Tell the workspace that we're done binding items
             r = new Runnable() {
@@ -2695,11 +2554,23 @@
 
                 }
             };
+            deferredExecutor.execute(r);
+
             if (isLoadingSynchronously) {
-                synchronized (mDeferredBindRunnables) {
-                    mDeferredBindRunnables.add(r);
-                }
-            } else {
+                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);
             }
         }
@@ -2766,7 +2637,6 @@
                     final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                     if (callbacks != null) {
                         callbacks.bindAllApplications(list);
-                        callbacks.bindAllPackages(mFgWidgetsModel);
                     }
                     if (DEBUG_LOADERS) {
                         Log.d(TAG, "bound all " + list.size() + " apps from cache in "
@@ -2810,12 +2680,12 @@
                 if (apps == null || apps.isEmpty()) {
                     return;
                 }
-
+                boolean quietMode = mUserManager.isQuietModeEnabled(user);
                 // Create the ApplicationInfos
                 for (int i = 0; i < apps.size(); i++) {
                     LauncherActivityInfoCompat app = apps.get(i);
                     // This builds the icon bitmaps.
-                    mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache));
+                    mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, quietMode));
                 }
 
                 final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
@@ -2858,7 +2728,7 @@
                         callbacks.bindAllApplications(added);
                         if (DEBUG_LOADERS) {
                             Log.d(TAG, "bound " + added.size() + " apps in "
-                                + (SystemClock.uptimeMillis() - bindTime) + "ms");
+                                    + (SystemClock.uptimeMillis() - bindTime) + "ms");
                         }
                     } else {
                         Log.i(TAG, "not binding apps: no Launcher activity");
@@ -2867,8 +2737,6 @@
             });
             // Cleanup any data stored for a deleted user.
             ManagedProfileHeuristic.processAllUsers(profiles, mContext);
-
-            loadAndBindWidgetsAndShortcuts(tryGetCallbacks(oldCallbacks), true /* refresh */);
             if (DEBUG_LOADERS) {
                 Log.d(TAG, "Icons processed in "
                         + (SystemClock.uptimeMillis() - loadTime) + "ms");
@@ -2935,9 +2803,6 @@
                 }
             });
         }
-
-        // Reload widget list. No need to refresh, as we only want to update the icons and labels.
-        loadAndBindWidgetsAndShortcuts(callbacks, false);
     }
 
     void enqueuePackageUpdated(PackageUpdatedTask task) {
@@ -2960,13 +2825,9 @@
                     packagesUnavailable.clear();
                     for (String pkg : entry.getValue()) {
                         if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
-                            boolean packageOnSdcard = launcherApps.isAppEnabled(
-                                    manager, pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
-                            if (packageOnSdcard) {
-                                Launcher.addDumpLog(TAG, "Package found on sd-card: " + pkg, true);
+                            if (PackageManagerHelper.isAppOnSdcard(manager, pkg)) {
                                 packagesUnavailable.add(pkg);
                             } else {
-                                Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
                                 packagesRemoved.add(pkg);
                             }
                         }
@@ -2995,7 +2856,9 @@
         public static final int OP_UPDATE = 2;
         public static final int OP_REMOVE = 3; // uninstlled
         public static final int OP_UNAVAILABLE = 4; // external media unmounted
-
+        public static final int OP_SUSPEND = 5; // package suspended
+        public static final int OP_UNSUSPEND = 6; // package unsuspended
+        public static final int OP_USER_AVAILABILITY_CHANGE = 7; // user available/unavailable
 
         public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) {
             mOp = op;
@@ -3012,6 +2875,8 @@
 
             final String[] packages = mPackages;
             final int N = packages.length;
+            FlagOp flagOp = FlagOp.NO_OP;
+            StringFilter pkgFilter = StringFilter.of(new HashSet<>(Arrays.asList(packages)));
             switch (mOp) {
                 case OP_ADD: {
                     for (int i=0; i<N; i++) {
@@ -3033,6 +2898,8 @@
                         mBgAllAppsList.updatePackage(context, packages[i], mUser);
                         mApp.getWidgetCache().removePackage(packages[i], mUser);
                     }
+                    // Since package was just updated, the target must be available now.
+                    flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
                     break;
                 case OP_REMOVE: {
                     ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
@@ -3051,6 +2918,23 @@
                         mBgAllAppsList.removePackage(packages[i], mUser);
                         mApp.getWidgetCache().removePackage(packages[i], mUser);
                     }
+                    flagOp = FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
+                    break;
+                case OP_SUSPEND:
+                case OP_UNSUSPEND:
+                    flagOp = mOp == OP_SUSPEND ?
+                            FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED) :
+                                    FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED);
+                    if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.(un)suspend " + N);
+                    mBgAllAppsList.updatePackageFlags(pkgFilter, mUser, flagOp);
+                    break;
+                case OP_USER_AVAILABILITY_CHANGE:
+                    flagOp = UserManagerCompat.getInstance(context).isQuietModeEnabled(mUser)
+                            ? FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER)
+                            : FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER);
+                    // We want to update all packages for this user.
+                    pkgFilter = StringFilter.matchesAll();
+                    mBgAllAppsList.updatePackageFlags(pkgFilter, mUser, flagOp);
                     break;
             }
 
@@ -3059,11 +2943,11 @@
             final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
 
             if (mBgAllAppsList.added.size() > 0) {
-                added = new ArrayList<AppInfo>(mBgAllAppsList.added);
+                added = new ArrayList<>(mBgAllAppsList.added);
                 mBgAllAppsList.added.clear();
             }
             if (mBgAllAppsList.modified.size() > 0) {
-                modified = new ArrayList<AppInfo>(mBgAllAppsList.modified);
+                modified = new ArrayList<>(mBgAllAppsList.modified);
                 mBgAllAppsList.modified.clear();
             }
             if (mBgAllAppsList.removed.size() > 0) {
@@ -3071,14 +2955,7 @@
                 mBgAllAppsList.removed.clear();
             }
 
-            final Callbacks callbacks = getCallback();
-            if (callbacks == null) {
-                Log.w(TAG, "Nobody to tell about the new app.  Launcher is probably loading.");
-                return;
-            }
-
-            final HashMap<ComponentName, AppInfo> addedOrUpdatedApps =
-                    new HashMap<ComponentName, AppInfo>();
+            final HashMap<ComponentName, AppInfo> addedOrUpdatedApps = new HashMap<>();
 
             if (added != null) {
                 addAppsToAllApps(context, added);
@@ -3088,6 +2965,7 @@
             }
 
             if (modified != null) {
+                final Callbacks callbacks = getCallback();
                 final ArrayList<AppInfo> modifiedFinal = modified;
                 for (AppInfo ai : modified) {
                     addedOrUpdatedApps.put(ai.componentName, ai);
@@ -3104,12 +2982,11 @@
             }
 
             // Update shortcut infos
-            if (mOp == OP_ADD || mOp == OP_UPDATE) {
+            if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
                 final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<ShortcutInfo>();
                 final ArrayList<ShortcutInfo> removedShortcuts = new ArrayList<ShortcutInfo>();
                 final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<LauncherAppWidgetInfo>();
 
-                HashSet<String> packageSet = new HashSet<String>(Arrays.asList(packages));
                 synchronized (sBgLock) {
                     for (ItemInfo info : sBgItemsIdMap) {
                         if (info instanceof ShortcutInfo && mUser.equals(info.user)) {
@@ -3119,7 +2996,7 @@
 
                             // Update shortcuts which use iconResource.
                             if ((si.iconResource != null)
-                                    && packageSet.contains(si.iconResource.packageName)) {
+                                    && pkgFilter.matches(si.iconResource.packageName)) {
                                 Bitmap icon = Utilities.createIconBitmap(
                                         si.iconResource.packageName,
                                         si.iconResource.resourceName, context);
@@ -3131,7 +3008,7 @@
                             }
 
                             ComponentName cn = si.getTargetComponent();
-                            if (cn != null && packageSet.contains(cn.getPackageName())) {
+                            if (cn != null && pkgFilter.matches(cn.getPackageName())) {
                                 AppInfo appInfo = addedOrUpdatedApps.get(cn);
 
                                 if (si.isPromise()) {
@@ -3179,9 +3056,9 @@
                                     infoUpdated = true;
                                 }
 
-                                if ((si.isDisabled & ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE) != 0) {
-                                    // Since package was just updated, the target must be available now.
-                                    si.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
+                                int oldDisabledFlags = si.isDisabled;
+                                si.isDisabled = flagOp.apply(si.isDisabled);
+                                if (si.isDisabled != oldDisabledFlags) {
                                     shortcutUpdated = true;
                                 }
                             }
@@ -3192,11 +3069,11 @@
                             if (infoUpdated) {
                                 updateItemInDatabase(context, si);
                             }
-                        } else if (info instanceof LauncherAppWidgetInfo) {
+                        } else if (info instanceof LauncherAppWidgetInfo && mOp == OP_ADD) {
                             LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
                             if (mUser.equals(widgetInfo.user)
                                     && widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
-                                    && packageSet.contains(widgetInfo.providerName.getPackageName())) {
+                                    && pkgFilter.matches(widgetInfo.providerName.getPackageName())) {
                                 widgetInfo.restoreStatus &=
                                         ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY &
                                         ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
@@ -3214,6 +3091,7 @@
                 }
 
                 if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) {
+                    final Callbacks callbacks = getCallback();
                     mHandler.post(new Runnable() {
 
                         public void run() {
@@ -3229,6 +3107,7 @@
                     }
                 }
                 if (!widgets.isEmpty()) {
+                    final Callbacks callbacks = getCallback();
                     mHandler.post(new Runnable() {
                         public void run() {
                             Callbacks cb = getCallback();
@@ -3240,222 +3119,109 @@
                 }
             }
 
-            final ArrayList<String> removedPackageNames =
-                    new ArrayList<String>();
-            if (mOp == OP_REMOVE || mOp == OP_UNAVAILABLE) {
+            final HashSet<String> removedPackages = new HashSet<>();
+            final HashSet<ComponentName> removedComponents = new HashSet<>();
+            if (mOp == OP_REMOVE) {
                 // Mark all packages in the broadcast to be removed
-                removedPackageNames.addAll(Arrays.asList(packages));
+                Collections.addAll(removedPackages, packages);
+
+                // No need to update the removedComponents as
+                // removedPackages is a super-set of removedComponents
             } else if (mOp == OP_UPDATE) {
                 // Mark disabled packages in the broadcast to be removed
                 for (int i=0; i<N; i++) {
                     if (isPackageDisabled(context, packages[i], mUser)) {
-                        removedPackageNames.add(packages[i]);
+                        removedPackages.add(packages[i]);
                     }
                 }
+
+                // Update removedComponents as some components can get removed during package update
+                for (AppInfo info : removedApps) {
+                    removedComponents.add(info.componentName);
+                }
             }
 
-            if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) {
-                final int removeReason;
-                if (mOp == OP_UNAVAILABLE) {
-                    removeReason = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
-                } else {
-                    // Remove all the components associated with this package
-                    for (String pn : removedPackageNames) {
-                        deletePackageFromDatabase(context, pn, mUser);
-                    }
-                    // Remove all the specific components
-                    for (AppInfo a : removedApps) {
-                        ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
-                        deleteItemsFromDatabase(context, infos);
-                    }
-                    removeReason = 0;
+            if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
+                for (String pn : removedPackages) {
+                    deletePackageFromDatabase(context, pn, mUser);
+                }
+                for (ComponentName cn : removedComponents) {
+                    deleteItemsFromDatabase(context, getItemInfoForComponentName(cn, mUser));
                 }
 
                 // Remove any queued items from the install queue
-                InstallShortcutReceiver.removeFromInstallQueue(context, removedPackageNames, mUser);
+                InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
+
                 // Call the components-removed callback
+                final Callbacks callbacks = getCallback();
                 mHandler.post(new Runnable() {
                     public void run() {
                         Callbacks cb = getCallback();
                         if (callbacks == cb && cb != null) {
-                            callbacks.bindComponentsRemoved(
-                                    removedPackageNames, removedApps, mUser, removeReason);
+                            callbacks.bindWorkspaceComponentsRemoved(
+                                    removedPackages, removedComponents, mUser);
                         }
                     }
                 });
             }
 
-            // Update widgets
-            if (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE) {
-                // Always refresh for a package event on secondary user
-                boolean needToRefresh = !mUser.equals(UserHandleCompat.myUserHandle());
-
-                // Refresh widget list, if the package already had a widget.
-                synchronized (sBgLock) {
-                    if (sBgWidgetProviders != null) {
-                        HashSet<String> pkgSet = new HashSet<>();
-                        Collections.addAll(pkgSet, mPackages);
-
-                        for (ComponentKey key : sBgWidgetProviders.keySet()) {
-                            needToRefresh |= key.user.equals(mUser) &&
-                                    pkgSet.contains(key.componentName.getPackageName());
+            if (!removedApps.isEmpty()) {
+                // Remove corresponding apps from All-Apps
+                final Callbacks callbacks = getCallback();
+                mHandler.post(new Runnable() {
+                    public void run() {
+                        Callbacks cb = getCallback();
+                        if (callbacks == cb && cb != null) {
+                            callbacks.bindAppInfosRemoved(removedApps);
                         }
                     }
-                }
+                });
+            }
 
-                if (!needToRefresh && mOp != OP_REMOVE) {
-                    // Refresh widget list, if there is any newly added widget
-                    PackageManager pm = context.getPackageManager();
-                    for (String pkg : mPackages) {
-                        try {
-                            List<ResolveInfo> widgets = pm.queryBroadcastReceivers(
-                                    new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
-                                            .setPackage(pkg), 0);
-                            needToRefresh |= widgets != null && !widgets.isEmpty();
-                        } catch (RuntimeException e) {
-                            if (LauncherAppState.isDogfoodBuild()) {
-                                throw e;
-                            }
-                            // Ignore the crash. We can live with a state widget list.
-                            Log.e(TAG, "PM call failed for " + pkg, e);
+            // Notify launcher of widget update. From marshmallow onwards we use AppWidgetHost to
+            // get widget update signals.
+            if (!Utilities.ATLEAST_MARSHMALLOW &&
+                    (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE)) {
+                final Callbacks callbacks = getCallback();
+                mHandler.post(new Runnable() {
+                    public void run() {
+                        Callbacks cb = getCallback();
+                        if (callbacks == cb && cb != null) {
+                            callbacks.notifyWidgetProvidersChanged();
                         }
                     }
-                }
-
-                loadAndBindWidgetsAndShortcuts(callbacks, needToRefresh);
-            }
-
-            // Write all the logs to disk
-            mHandler.post(new Runnable() {
-                public void run() {
-                    Callbacks cb = getCallback();
-                    if (callbacks == cb && cb != null) {
-                        callbacks.dumpLogsToLocalData();
-                    }
-                }
-            });
-        }
-    }
-
-    public static List<LauncherAppWidgetProviderInfo> getWidgetProviders(Context context,
-            boolean refresh) {
-        ArrayList<LauncherAppWidgetProviderInfo> results =
-                new ArrayList<LauncherAppWidgetProviderInfo>();
-        try {
-            synchronized (sBgLock) {
-                if (sBgWidgetProviders == null || refresh) {
-                    HashMap<ComponentKey, LauncherAppWidgetProviderInfo> tmpWidgetProviders
-                            = new HashMap<>();
-                    AppWidgetManagerCompat wm = AppWidgetManagerCompat.getInstance(context);
-                    LauncherAppWidgetProviderInfo info;
-
-                    List<AppWidgetProviderInfo> widgets = wm.getAllProviders();
-                    for (AppWidgetProviderInfo pInfo : widgets) {
-                        info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo);
-                        UserHandleCompat user = wm.getUser(info);
-                        tmpWidgetProviders.put(new ComponentKey(info.provider, user), info);
-                    }
-
-                    Collection<CustomAppWidget> customWidgets = Launcher.getCustomAppWidgets().values();
-                    for (CustomAppWidget widget : customWidgets) {
-                        info = new LauncherAppWidgetProviderInfo(context, widget);
-                        UserHandleCompat user = wm.getUser(info);
-                        tmpWidgetProviders.put(new ComponentKey(info.provider, user), info);
-                    }
-                    // Replace the global list at the very end, so that if there is an exception,
-                    // previously loaded provider list is used.
-                    sBgWidgetProviders = tmpWidgetProviders;
-                }
-                results.addAll(sBgWidgetProviders.values());
-                return results;
-            }
-        } catch (Exception e) {
-            if (!LauncherAppState.isDogfoodBuild() &&
-                    (e.getCause() instanceof TransactionTooLargeException ||
-                    e.getCause() instanceof DeadObjectException)) {
-                // the returned value may be incomplete and will not be refreshed until the next
-                // time Launcher starts.
-                // TODO: after figuring out a repro step, introduce a dirty bit to check when
-                // onResume is called to refresh the widget provider list.
-                synchronized (sBgLock) {
-                    if (sBgWidgetProviders != null) {
-                        results.addAll(sBgWidgetProviders.values());
-                    }
-                    return results;
-                }
-            } else {
-                throw e;
+                });
             }
         }
     }
 
-    public static LauncherAppWidgetProviderInfo getProviderInfo(Context ctx, ComponentName name,
-            UserHandleCompat user) {
-        synchronized (sBgLock) {
-            if (sBgWidgetProviders == null) {
-                getWidgetProviders(ctx, false /* refresh */);
-            }
-            return sBgWidgetProviders.get(new ComponentKey(name, user));
-        }
-    }
-
-    public void loadAndBindWidgetsAndShortcuts(final Callbacks callbacks, final boolean refresh) {
-
-        runOnWorkerThread(new Runnable() {
+    private void bindWidgetsModel(final Callbacks callbacks, final WidgetsModel model) {
+        mHandler.post(new Runnable() {
             @Override
             public void run() {
-                updateWidgetsModel(refresh);
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        Callbacks cb = getCallback();
-                        if (callbacks == cb && cb != null) {
-                            callbacks.bindAllPackages(mFgWidgetsModel);
-                        }
-                    }
-                });
-                // update the Widget entries inside DB on the worker thread.
-                LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(
-                        mFgWidgetsModel.getRawList());
+                Callbacks cb = getCallback();
+                if (callbacks == cb && cb != null) {
+                    callbacks.bindWidgetsModel(model);
+                }
             }
         });
     }
 
-    /**
-     * Returns a list of ResolveInfos/AppWidgetInfos.
-     *
-     * @see #loadAndBindWidgetsAndShortcuts
-     */
-    @Thunk void updateWidgetsModel(boolean refresh) {
-        Utilities.assertWorkerThread();
-        PackageManager packageManager = mApp.getContext().getPackageManager();
-        final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
-        widgetsAndShortcuts.addAll(getWidgetProviders(mApp.getContext(), refresh));
-
-        // Update shortcut providers
-        synchronized (sBgLock) {
-            try {
-                Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
-                List<ResolveInfo> providers = packageManager.queryIntentActivities(shortcutsIntent, 0);
-                sBgShortcutProviders = providers;
-            } catch (RuntimeException e) {
-                if (!LauncherAppState.isDogfoodBuild() &&
-                        (e.getCause() instanceof TransactionTooLargeException ||
-                                e.getCause() instanceof DeadObjectException)) {
-                    /**
-                     * Ignore exception and use the cached list if available.
-                     * Refer to {@link #getWidgetProviders(Context, boolean}} for more info.
-                     */
-                } else {
-                    throw e;
+    public void refreshAndBindWidgetsAndShortcuts(
+            final Callbacks callbacks, final boolean bindFirst) {
+        runOnWorkerThread(new Runnable() {
+            @Override
+            public void run() {
+                if (bindFirst && !mBgWidgetsModel.isEmpty()) {
+                    bindWidgetsModel(callbacks, mBgWidgetsModel.clone());
                 }
+                final WidgetsModel model = mBgWidgetsModel.updateAndClone(mApp.getContext());
+                bindWidgetsModel(callbacks, model);
+                // update the Widget entries inside DB on the worker thread.
+                LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(
+                        model.getRawList());
             }
-            if (sBgShortcutProviders != null) {
-                widgetsAndShortcuts.addAll(sBgShortcutProviders);
-            }
-        }
-        mBgWidgetsModel.setWidgetsAndShortcuts(widgetsAndShortcuts);
-        mFgWidgetsModel = mBgWidgetsModel.clone();
+        });
     }
 
     @Thunk static boolean isPackageDisabled(Context context, String packageName,
@@ -3545,7 +3311,7 @@
      *
      * If c is not null, then it will be used to fill in missing data like the title and icon.
      */
-    public ShortcutInfo getAppShortcutInfo(PackageManager manager, Intent intent,
+    public ShortcutInfo getAppShortcutInfo(Intent intent,
             UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
             boolean allowMissingTarget, boolean useLowResIcon) {
         if (user == null) {
@@ -3575,6 +3341,10 @@
             info.setIcon(icon == null ? mIconCache.getDefaultIcon(user) : icon);
         }
 
+        if (lai != null && PackageManagerHelper.isAppSuspended(lai.getApplicationInfo())) {
+            info.isDisabled = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+        }
+
         // from the db
         if (TextUtils.isEmpty(info.title) && c != null) {
             info.title =  Utilities.trim(c.getString(titleIndex));
@@ -3703,7 +3473,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;
@@ -3756,6 +3525,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 4760930..7ebee31 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -42,26 +42,31 @@
 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.UserManager;
+import android.provider.BaseColumns;
 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.ProviderConfig;
+import com.android.launcher3.dynamicui.ExtractionUtils;
 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;
 
 public class LauncherProvider extends ContentProvider {
     private static final String TAG = "LauncherProvider";
@@ -71,36 +76,29 @@
 
     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;
-    @Thunk DatabaseHelper mOpenHelper;
+    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);
+        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
@@ -113,9 +111,19 @@
         }
     }
 
+    /**
+     * Overridden in tests
+     */
+    protected synchronized void createDbIfNotExists() {
+        if (mOpenHelper == null) {
+            mOpenHelper = new DatabaseHelper(getContext(), mListenerHandler);
+        }
+    }
+
     @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();
@@ -151,11 +159,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;
             }
         }
@@ -185,9 +194,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();
@@ -213,6 +275,7 @@
     @Override
     public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
             throws OperationApplicationException {
+        createDbIfNotExists();
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         db.beginTransaction();
         try {
@@ -227,6 +290,7 @@
 
     @Override
     public int delete(Uri uri, String selection, String[] selectionArgs) {
+        createDbIfNotExists();
         SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
 
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
@@ -239,6 +303,7 @@
 
     @Override
     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        createDbIfNotExists();
         SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
 
         addModifiedTime(values);
@@ -251,32 +316,73 @@
     }
 
     @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();
-                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_DELETE_EMPTY_FOLDERS: {
                 Bundle result = new Bundle();
-                result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, value);
+                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_MIGRATE_LAUNCHER2_SHORTCUTS: {
+                mOpenHelper.migrateLauncher2Shortcuts(mOpenHelper.getWritableDatabase(),
+                        Uri.parse(getContext().getString(R.string.old_launcher_provider_uri)));
+                Utilities.getPrefs(getContext()).edit().putBoolean(EMPTY_DATABASE_CREATED, false)
+                        .commit();
+                return null;
+            }
+            case LauncherSettings.Settings.METHOD_UPDATE_FOLDER_ITEMS_RANK: {
+                mOpenHelper.updateFolderItemsRank(mOpenHelper.getWritableDatabase(), false);
+                return null;
+            }
+            case LauncherSettings.Settings.METHOD_CONVERT_SHORTCUTS_TO_ACTIVITIES: {
+                mOpenHelper.convertShortcutsToLauncherActivities(mOpenHelper.getWritableDatabase());
+                return null;
+            }
+            case LauncherSettings.Settings.METHOD_DELETE_DB: {
+                // Are you sure? (y/n)
+                mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
+                return null;
+            }
         }
         return null;
     }
@@ -285,8 +391,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 {
@@ -295,16 +401,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();
@@ -317,34 +423,27 @@
         return folderIds;
     }
 
-    private void notifyListeners() {
+    /**
+     * 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();
     }
 
@@ -355,16 +454,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());
@@ -373,7 +472,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);
                     }
                 }
@@ -381,7 +480,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
@@ -393,7 +492,7 @@
                 // Unable to load external layout. Cleanup and load the internal layout.
                 createEmptyDB();
                 mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(),
-                        getDefaultLayoutParser());
+                        getDefaultLayoutParser(widgetHost));
             }
             clearFlagEmptyDbCreated();
         }
@@ -405,7 +504,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;
@@ -424,7 +523,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;
@@ -433,57 +532,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());
-    }
-
-    private static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback {
+    /**
+     * The class is subclassed in tests to create an in-memory db.
+     */
+    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) {
@@ -506,87 +596,52 @@
             }
         }
 
-        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);
-            setFlagEmptyDbCreated();
+            onEmptyDbCreated();
+        }
+
+        /**
+         * 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.sendEmptyMessage(
+                        ChangeListenerWrapper.MSG_APP_WIDGET_HOST_RESET);
+            }
+
+            // Set the flag for empty DB
+            Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit();
 
             // When a new DB is created, remove all previously stored managed profile information.
-            ManagedProfileHeuristic.processAllUsers(Collections.<UserHandleCompat>emptyList(), mContext);
+            ManagedProfileHeuristic.processAllUsers(Collections.<UserHandleCompat>emptyList(),
+                    mContext);
+        }
+
+        protected long getDefaultUserSerial() {
+            return UserManagerCompat.getInstance(mContext).getSerialNumberForUser(
+                    UserHandleCompat.myUserHandle());
         }
 
         private void addFavoritesTable(SQLiteDatabase db, boolean optional) {
-            UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
-            long userSerialNumber = userManager.getSerialNumberForUser(
-                    UserHandleCompat.myUserHandle());
-            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 " + userSerialNumber + "," +
-                    "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" +
@@ -597,10 +652,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;
@@ -609,7 +664,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 +
@@ -618,20 +673,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();
-        }
-
-        private void setFlagEmptyDbCreated() {
-            Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit();
-        }
-
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
             if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion);
@@ -751,8 +798,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);
         }
 
@@ -767,9 +814,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,
@@ -819,7 +865,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);
@@ -837,7 +883,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
@@ -847,7 +893,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;
@@ -894,12 +940,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) {
@@ -933,12 +974,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);
@@ -946,7 +987,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
@@ -963,94 +1004,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) {
@@ -1066,7 +1020,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");
                 }
@@ -1101,8 +1055,6 @@
                                 = 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
@@ -1119,10 +1071,6 @@
                                 = 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);
 
@@ -1170,17 +1118,10 @@
                             }
 
                             if (userHandle == null) {
-                                Launcher.addDumpLog(TAG, "skipping deleted user", true);
+                                Log.d(TAG, "skipping deleted user");
                                 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;
@@ -1189,22 +1130,20 @@
                                     intent = Intent.parseUri(intentStr, 0);
                                 } catch (URISyntaxException e) {
                                     // bogus intent?
-                                    Launcher.addDumpLog(TAG,
-                                            "skipping invalid intent uri", true);
+                                    Log.d(TAG, "skipping invalid intent uri");
                                     continue;
                                 }
 
                                 cn = intent.getComponent();
                                 if (TextUtils.isEmpty(intentStr)) {
                                     // no intent? no icon
-                                    Launcher.addDumpLog(TAG, "skipping empty intent", true);
+                                    Log.d(TAG, "skipping empty intent");
                                     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);
+                                    Log.d(TAG, "skipping item whose component no longer exists.");
                                     continue;
                                 } else if (container ==
                                         LauncherSettings.Favorites.CONTAINER_DESKTOP) {
@@ -1220,7 +1159,7 @@
                                     final String key = intent.toUri(0);
                                     intent.setFlags(flags);
                                     if (seenIntents.contains(key)) {
-                                        Launcher.addDumpLog(TAG, "skipping duplicate", true);
+                                        Log.d(TAG, "skipping duplicate");
                                         continue;
                                     } else {
                                         seenIntents.add(key);
@@ -1232,8 +1171,6 @@
                             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));
@@ -1241,9 +1178,6 @@
                                     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) {
@@ -1327,7 +1261,7 @@
                             try {
                                 for (ContentValues row: allItems) {
                                     if (row == null) continue;
-                                    if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, row)
+                                    if (dbInsertAndCheck(this, db, Favorites.TABLE_NAME, null, row)
                                             < 0) {
                                         return;
                                     } else {
@@ -1346,7 +1280,7 @@
                                 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)
+                                if (dbInsertAndCheck(this, db, WorkspaceScreens.TABLE_NAME, null, values)
                                         < 0) {
                                     return;
                                 }
@@ -1363,11 +1297,8 @@
                 }
             }
 
-            Launcher.addDumpLog(TAG, "migrated " + count + " icons from Launcher2 into "
-                    + (curScreen+1) + " screens", true);
-
-            // ensure that new screens are created to hold these icons
-            setFlagJustLoadedOldDb();
+            Log.d(TAG, "migrated " + count + " icons from Launcher2 into "
+                    + (curScreen+1) + " screens");
 
             // Update max IDs; very important since we just grabbed IDs from another database
             mMaxItemId = initializeMaxItemId(db);
@@ -1428,4 +1359,31 @@
             }
         }
     }
+
+    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:
+                        mListener.onAppWidgetHostReset();
+                        break;
+                }
+            }
+            return true;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java
index 1b78e9c..524befc 100644
--- a/src/com/android/launcher3/LauncherProviderChangeListener.java
+++ b/src/com/android/launcher3/LauncherProviderChangeListener.java
@@ -9,7 +9,7 @@
 
     public void onLauncherProviderChange();
 
-    public void onSettingsChanged(String settings, boolean value);
+    public void onExtractedColorsChanged();
 
     public void onAppWidgetHostReset();
 }
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 55a512f..104af52 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -9,11 +9,14 @@
 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;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mDrawRightInsetBar;
+    @ViewDebug.ExportedProperty(category = "launcher")
     private int mRightInsetBarWidth;
 
     private View mAlignedView;
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 01d670d..45a87cc 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";
@@ -113,7 +100,7 @@
         /**
          * The content:// style URL for this table
          */
-        static final Uri CONTENT_URI = Uri.parse("content://" +
+        public static final Uri CONTENT_URI = Uri.parse("content://" +
                 ProviderConfig.AUTHORITY + "/" + TABLE_NAME);
 
         /**
@@ -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,24 +211,6 @@
         public static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5;
 
         /**
-         * The favorite is a clock
-         */
-        @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;
-
-        /**
          * The appWidgetId of the widget
          *
          * <P>Type: INTEGER</P>
@@ -264,33 +223,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 +241,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 +277,31 @@
         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_DELETE_EMPTY_FOLDERS = "delete_empty_folders";
+        public static final String METHOD_UPDATE_FOLDER_ITEMS_RANK = "update_folder_items_rank";
+        public static final String METHOD_CONVERT_SHORTCUTS_TO_ACTIVITIES =
+                "convert_shortcuts_to_launcher_activities";
+
+        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_MIGRATE_LAUNCHER2_SHORTCUTS = "migrate_l2_shortcuts";
+
+        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 30cae31..17a5424 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -24,15 +24,17 @@
 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.util.Thunk;
+import com.android.launcher3.util.UiThreadCircularReveal;
 import com.android.launcher3.widget.WidgetsContainerView;
 
 import java.util.HashMap;
@@ -146,6 +148,7 @@
             }
             @Override
             void onTransitionComplete() {
+                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
                 if (startSearchAfterTransition) {
                     toView.startAppsSearch();
                 }
@@ -163,18 +166,22 @@
             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));
+                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) {
@@ -182,10 +189,14 @@
         }
 
         if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
-            startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState, toWorkspacePage,
+            startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState,
+                    animated, 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);
         }
     }
@@ -214,16 +225,8 @@
         // 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);
+        playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+                animated, initialized, animation, revealDuration, layerViews);
 
         final View contentView = toView.getContentView();
 
@@ -257,11 +260,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);
@@ -328,13 +331,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 +350,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();
                         }
                     }
@@ -394,6 +390,34 @@
     }
 
     /**
+     * Plays animations used by various transitions.
+     */
+    private void playCommonTransitionAnimations(
+            Workspace.State toWorkspaceState, View fromView, View toView,
+            boolean animated, boolean initialized, AnimatorSet animation, int revealDuration,
+            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);
+
+        // Animate the search bar
+        final SearchDropTargetBar.State toSearchBarState =
+                toWorkspaceState.searchDropTargetBarState;
+        mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState,
+                animated ? revealDuration : 0, animation);
+
+        if (animated && initialized) {
+            // Play the workspace animation
+            if (workspaceAnim != null) {
+                animation.play(workspaceAnim);
+            }
+            // Dispatch onLauncherTransitionStep() as the animation interpolates.
+            animation.play(dispatchOnLauncherTransitionStepAnim(fromView, toView));
+        }
+    }
+
+    /**
      * Returns an Animator that calls {@link #dispatchOnLauncherTransitionStep(View, float)} on
      * {@param fromView} and {@param toView} as the animation interpolates.
      *
@@ -412,11 +436,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, 
+            final Runnable onCompleteRunnable) {
         AllAppsContainerView appsView = mLauncher.getAppsView();
         // No alpha anim from all apps
         PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
@@ -444,19 +468,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,
+                mLauncher.getAllAppsButton(), appsView,
                 animated, 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,19 +498,105 @@
                     }
                 };
             }
+            @Override
+            void onTransitionComplete() {
+                mLauncher.getUserEventDispatcher().resetElapsedContainerMillis();
+            }
         };
         mCurrentAnimation = startAnimationToWorkspaceFromOverlay(
                 fromWorkspaceState, toWorkspaceState,
-                toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView,
+                mLauncher.getWidgetsButton(), widgetsView,
                 animated, 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, revealDuration, 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(
             final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
-            final int toWorkspacePage,
             final View buttonView, final BaseContainerView fromView,
             final boolean animated, final Runnable onCompleteRunnable,
             final PrivateTransitionCallbacks pCb) {
@@ -503,24 +617,12 @@
         // 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);
+        playCommonTransitionAnimations(toWorkspaceState, fromView, toView,
+                animated, initialized, animation, revealDuration, layerViews);
 
         if (animated && initialized) {
-            // Play the workspace animation
-            if (workspaceAnim != null) {
-                animation.play(workspaceAnim);
-            }
-
-            animation.play(updateTransitionStepAnim);
             final View revealView = fromView.getRevealView();
             final View contentView = fromView.getContentView();
 
@@ -599,6 +701,17 @@
                 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();
@@ -616,8 +729,8 @@
                 }
             }
 
-            dispatchOnLauncherTransitionPrepare(fromView, animated, true);
-            dispatchOnLauncherTransitionPrepare(toView, animated, true);
+            dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible);
+            dispatchOnLauncherTransitionPrepare(toView, animated, multiplePagesVisible);
 
             animation.addListener(new AnimatorListenerAdapter() {
                 @Override
@@ -653,6 +766,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,7 +781,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();
                         }
                     }
@@ -677,12 +791,12 @@
             fromView.post(startAnimRunnable);
 
             return animation;
-        } else {
+        } else /* if (!(animated && initialized)) */ {
             fromView.setVisibility(View.GONE);
-            dispatchOnLauncherTransitionPrepare(fromView, animated, true);
+            dispatchOnLauncherTransitionPrepare(fromView, animated, multiplePagesVisible);
             dispatchOnLauncherTransitionStart(fromView, animated, true);
             dispatchOnLauncherTransitionEnd(fromView, animated, true);
-            dispatchOnLauncherTransitionPrepare(toView, animated, true);
+            dispatchOnLauncherTransitionPrepare(toView, animated, multiplePagesVisible);
             dispatchOnLauncherTransitionStart(toView, animated, true);
             dispatchOnLauncherTransitionEnd(toView, animated, true);
             pCb.onTransitionComplete();
@@ -697,22 +811,13 @@
     }
 
     /**
-     * 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.getSearchDropTargetBarState();
-        mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration, animation);
-    }
-
-    /**
      * 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/LogAccelerateInterpolator.java b/src/com/android/launcher3/LogAccelerateInterpolator.java
index c3bbfa5..519d391 100644
--- a/src/com/android/launcher3/LogAccelerateInterpolator.java
+++ b/src/com/android/launcher3/LogAccelerateInterpolator.java
@@ -20,6 +20,8 @@
 
     @Override
     public float getInterpolation(float t) {
-        return 1 - computeLog(1 - t, mBase, mDrift) * mLogScale;
+        // Due to rounding issues, the interpolation doesn't quite reach 1 even though it should.
+        // To account for this, we short-circuit to return 1 if the input is 1.
+        return Float.compare(t, 1f) == 0 ? 1f : 1 - computeLog(1 - t, mBase, mDrift) * mLogScale;
     }
 }
diff --git a/src/com/android/launcher3/LogDecelerateInterpolator.java b/src/com/android/launcher3/LogDecelerateInterpolator.java
index 4c5f6f0..7d95282 100644
--- a/src/com/android/launcher3/LogDecelerateInterpolator.java
+++ b/src/com/android/launcher3/LogDecelerateInterpolator.java
@@ -21,6 +21,8 @@
 
     @Override
     public float getInterpolation(float t) {
-        return computeLog(t, mBase, mDrift) * mLogScale;
+        // Due to rounding issues, the interpolation doesn't quite reach 1 even though it should.
+        // To account for this, we short-circuit to return 1 if the input is 1.
+        return Float.compare(t, 1f) == 0 ? 1f : computeLog(t, mBase, mDrift) * mLogScale;
     }
 }
diff --git a/src/com/android/launcher3/PageIndicator.java b/src/com/android/launcher3/PageIndicator.java
index 62ea03b..8adbf8d 100644
--- a/src/com/android/launcher3/PageIndicator.java
+++ b/src/com/android/launcher3/PageIndicator.java
@@ -21,6 +21,7 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
+import android.view.ViewDebug;
 import android.widget.LinearLayout;
 
 import java.util.ArrayList;
@@ -37,6 +38,7 @@
 
     private ArrayList<PageIndicatorMarker> mMarkers =
             new ArrayList<PageIndicatorMarker>();
+    @ViewDebug.ExportedProperty(category = "launcher")
     private int mActiveMarkerIndex;
 
     public static class PageMarkerResources {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 2dcff35..e1cb082 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,16 +42,15 @@
 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;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.Interpolator;
-
 import com.android.launcher3.util.LauncherEdgeEffect;
 import com.android.launcher3.util.Thunk;
-
 import java.util.ArrayList;
 
 /**
@@ -66,9 +65,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 +90,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 +112,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 +129,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 +142,19 @@
 
     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;
 
-    private boolean mWasInOverscroll = false;
+    protected boolean mWasInOverscroll = false;
 
     // Page Indicator
     @Thunk int mPageIndicatorViewId;
     @Thunk 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);
     }
@@ -355,7 +337,7 @@
     int getViewportWidth() {
         return mViewport.width();
     }
-    int getViewportHeight() {
+    public int getViewportHeight() {
         return mViewport.height();
     }
 
@@ -377,18 +359,10 @@
     }
 
     /**
-     * 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 +371,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 +400,7 @@
         }
         scrollTo(newX, 0);
         mScroller.setFinalX(newX);
-        forceFinishScroller();
+        forceFinishScroller(true);
     }
 
     private void abortScrollerAnimation(boolean resetNextPage) {
@@ -438,11 +412,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 +430,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.boundInRange(validatedPage, 0, getPageCount() - 1);
         return validatedPage;
     }
 
@@ -493,10 +469,6 @@
      * has settled.
      */
     protected void notifyPageSwitchListener() {
-        if (mPageSwitchListener != null) {
-            mPageSwitchListener.onPageSwitch(getPageAt(getNextPage()), getNextPage());
-        }
-
         updatePageIndicator();
     }
 
@@ -551,9 +523,13 @@
         super.setOnLongClickListener(l);
     }
 
+    protected int getUnboundedScrollX() {
+        return getScrollX();
+    }
+
     @Override
     public void scrollBy(int x, int y) {
-        scrollTo(getScrollX() + x, getScrollY() + y);
+        scrollTo(getUnboundedScrollX() + x, getScrollY() + y);
     }
 
     @Override
@@ -564,7 +540,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);
@@ -601,9 +577,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);
@@ -633,17 +606,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);
@@ -831,6 +810,7 @@
         setMeasuredDimension(scaledWidthSize, scaledHeightSize);
     }
 
+    @SuppressLint("DrawAllocation")
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         if (getChildCount() == 0) {
@@ -869,9 +849,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();
@@ -1051,16 +1029,16 @@
     }
 
     protected void getVisiblePages(int[] range) {
-        final int pageCount = getChildCount();
+        final int count = getChildCount();
         range[0] = -1;
         range[1] = -1;
 
-        if (pageCount > 0) {
+        if (count > 0) {
             final int visibleLeft = -getLeft();
             final int visibleRight = visibleLeft + getViewportWidth();
+            final Matrix pageShiftMatrix = getPageShiftMatrix();
             int curScreen = 0;
 
-            int count = getChildCount();
             for (int i = 0; i < count; i++) {
                 View currPage = getPageAt(i);
 
@@ -1069,7 +1047,7 @@
                 sTmpRectF.right = currPage.getMeasuredWidth();
                 currPage.getMatrix().mapRect(sTmpRectF);
                 sTmpRectF.offset(currPage.getLeft() - getScrollX(), 0);
-                getMatrix().mapRect(sTmpRectF);
+                pageShiftMatrix.mapRect(sTmpRectF);
 
                 if (sTmpRectF.left > visibleRight || sTmpRectF.right < visibleLeft) {
                     if (range[0] == -1) {
@@ -1078,7 +1056,6 @@
                         break;
                     }
                 }
-
                 curScreen = i;
                 if (range[0] < 0) {
                     range[0] = curScreen;
@@ -1092,6 +1069,10 @@
         }
     }
 
+    protected Matrix getPageShiftMatrix() {
+        return getMatrix();
+    }
+
     protected boolean shouldDrawChild(View child) {
         return child.getVisibility() == VISIBLE;
     }
@@ -1449,10 +1430,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);
         }
     }
 
@@ -1555,6 +1536,7 @@
     }
 
     private void setEnableFreeScroll(boolean freeScroll) {
+        boolean wasFreeScroll = mFreeScroll;
         mFreeScroll = freeScroll;
 
         if (mFreeScroll) {
@@ -1565,6 +1547,8 @@
             } else if (getCurrentPage() > mTempVisiblePagesRange[1]) {
                 setCurrentPage(mTempVisiblePagesRange[1]);
             }
+        } else if (wasFreeScroll) {
+            snapToPage(getNextPage());
         }
 
         setEnableOverscroll(!freeScroll);
@@ -1649,8 +1633,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;
@@ -1710,16 +1692,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);
                                 }
@@ -1801,6 +1781,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();
@@ -1961,9 +1942,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);
@@ -2008,7 +1993,7 @@
         int halfScreenSize = getViewportWidth() / 2;
 
         final int newX = getScrollForPage(whichPage);
-        int delta = newX - getScrollX();
+        int delta = newX - getUnboundedScrollX();
         int duration = 0;
 
         if (Math.abs(velocity) < mMinFlingVelocity) {
@@ -2041,7 +2026,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);
     }
 
@@ -2058,7 +2043,7 @@
         whichPage = validateNewPage(whichPage);
 
         int newX = getScrollForPage(whichPage);
-        final int delta = newX - getScrollX();
+        final int delta = newX - getUnboundedScrollX();
         snapToPage(whichPage, delta, duration, immediate, interpolator);
     }
 
@@ -2090,7 +2075,7 @@
             mScroller.setInterpolator(mDefaultInterpolator);
         }
 
-        mScroller.startScroll(getScrollX(), 0, delta, 0, duration);
+        mScroller.startScroll(getUnboundedScrollX(), 0, delta, 0, duration);
 
         updatePageIndicator();
 
@@ -2111,20 +2096,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;
@@ -2164,13 +2135,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) {
@@ -2267,9 +2237,8 @@
         animateDragViewToOriginalPosition();
     }
 
-    private static final int ANIM_TAG_KEY = 100;
-
     /* Accessibility */
+    @SuppressWarnings("deprecation")
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
@@ -2329,7 +2298,7 @@
     }
 
     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/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java
new file mode 100644
index 0000000..c8c8fa4
--- /dev/null
+++ b/src/com/android/launcher3/PinchAnimationManager.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;
+
+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_PAGE_INDICATOR = 0;
+    private static final int INDEX_HOTSEAT = 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) {
+                animateHotseatAndPageIndicator(goingTowards == NORMAL);
+                animateQsb(goingTowards == NORMAL);
+            }
+        } else if (threshold == PinchThresholdManager.THRESHOLD_TWO) {
+            if (startState == OVERVIEW) {
+                animateHotseatAndPageIndicator(goingTowards == NORMAL);
+                animateQsb(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 animateHotseatAndPageIndicator(boolean show) {
+        animateShowHideView(INDEX_HOTSEAT, mLauncher.getHotseat(), show);
+        if (mWorkspace.getPageIndicator() != null) {
+            // There aren't page indicators in landscape mode on phones, hence the null check.
+            animateShowHideView(INDEX_PAGE_INDICATOR, mWorkspace.getPageIndicator(), show);
+        }
+    }
+
+    private void animateQsb(boolean show) {
+        SearchDropTargetBar.State searchBarState = show ? SearchDropTargetBar.State.SEARCH_BAR
+                : SearchDropTargetBar.State.INVISIBLE;
+        mLauncher.getSearchDropTargetBar().animateToState(searchBarState, 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..0c8568e
--- /dev/null
+++ b/src/com/android/launcher3/PinchToOverviewListener.java
@@ -0,0 +1,211 @@
+/*
+ * 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;
+
+/**
+ * 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 {
+    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 void 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 {
+                mPinchDetector.onTouchEvent(ev);
+            }
+        }
+    }
+
+    @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 (mWorkspace.getOpenFolder() != null) {
+            // Don't listen for the pinch gesture if a folder 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/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java
index f7288a0..171dd87 100644
--- a/src/com/android/launcher3/SearchDropTargetBar.java
+++ b/src/com/android/launcher3/SearchDropTargetBar.java
@@ -24,25 +24,23 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.View;
+import android.view.ViewDebug;
 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.dragndrop.DragController;
 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.
+ * This bar will manage the transition between the QSB search bar and the delete/uninstall drop
+ * targets so that each of the individual ButtonDropTargets don't have to.
  */
-public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener {
+public class SearchDropTargetBar extends BaseDropTargetBar {
 
     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 {
@@ -63,18 +61,12 @@
 
     }
 
-    private static int DEFAULT_DRAG_FADE_DURATION = 175;
 
-    private AnimatorSet mCurrentAnimation;
-
+    @ViewDebug.ExportedProperty(category = "launcher")
     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;
 
@@ -86,40 +78,42 @@
         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);
+        mUninstallDropTarget = (ButtonDropTarget) mDropTargetBar
+                .findViewById(R.id.uninstall_target_text);
 
-        mInfoDropTarget.setSearchDropTargetBar(this);
-        mDeleteDropTarget.setSearchDropTargetBar(this);
-        mUninstallDropTarget.setSearchDropTargetBar(this);
+        mDeleteDropTarget.setDropTargetBar(this);
+        mUninstallDropTarget.setDropTargetBar(this);
+    }
 
-        // Create the various fade animations
-        mDropTargetBar.setAlpha(0f);
-        AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
+    @Override
+    public void setup(Launcher launcher, DragController dragController) {
+        dragController.addDragListener(this);
+        dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
+
+        dragController.addDragListener(mDeleteDropTarget);
+        dragController.addDragListener(mUninstallDropTarget);
+
+        dragController.addDropTarget(mDeleteDropTarget);
+        dragController.addDropTarget(mUninstallDropTarget);
+
+        mDeleteDropTarget.setLauncher(launcher);
+        mUninstallDropTarget.setLauncher(launcher);
+    }
+
+    @Override
+    public void showDropTargets() {
+        animateToState(State.DROP_TARGET, DEFAULT_DRAG_FADE_DURATION);
+    }
+
+    @Override
+    public void hideDropTargets() {
+        animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION);
     }
 
     public void setQsbSearchBar(View qsb) {
@@ -138,21 +132,8 @@
         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;
-
+            resetAnimation(duration);
             if (duration > 0) {
-                mCurrentAnimation = new AnimatorSet();
-                mCurrentAnimation.setDuration(duration);
-
                 animateAlpha(mDropTargetBar, mState.mDropTargetBarAlpha, DEFAULT_INTERPOLATOR);
             } else {
                 mDropTargetBar.setAlpha(mState.mDropTargetBarAlpha);
@@ -194,40 +175,6 @@
         }
     }
 
-    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.
      */
@@ -247,32 +194,12 @@
         }
     }
 
+    @Override
     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..5ef6dd5 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 = getContext().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(getContext()));
+            }
         }
 
         @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) {
+                getContext().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..008dd84 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -18,8 +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;
 import android.view.ViewGroup;
@@ -42,7 +40,6 @@
     private int mHeightGap;
 
     private int mCountX;
-    private int mCountY;
 
     private Launcher mLauncher;
 
@@ -61,7 +58,6 @@
         mWidthGap = widthGap;
         mHeightGap = heightGap;
         mCountX = countX;
-        mCountY = countY;
     }
 
     public View getChildAt(int x, int y) {
@@ -79,24 +75,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();
 
@@ -236,7 +214,6 @@
         }
     }
 
-    @Override
     protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
         super.setChildrenDrawnWithCacheEnabled(enabled);
     }
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 6bdcb4b..2cc9bb0 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -27,9 +27,9 @@
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.folder.FolderIcon;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 
 /**
  * Represents a launchable icon on the workspaces and in folders.
@@ -71,14 +71,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
@@ -113,6 +106,16 @@
     public static final int FLAG_DISABLED_NOT_AVAILABLE = 2;
 
     /**
+     * Indicates that the icon is disabled as the app is suspended
+     */
+    public static final int FLAG_DISABLED_SUSPENDED = 4;
+
+    /**
+     * Indicates that the icon is disabled as the user is in quiet mode.
+     */
+    public static final int FLAG_DISABLED_QUIET_USER = 8;
+
+    /**
      * Could be disabled, if the the app is installed but unavailable (eg. in safe mode or when
      * sd-card is not available).
      */
@@ -165,7 +168,6 @@
             iconResource.resourceName = info.iconResource.resourceName;
         }
         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;
@@ -176,8 +178,8 @@
         super(info);
         title = Utilities.trim(info.title);
         intent = new Intent(info.intent);
-        customIcon = false;
         flags = info.flags;
+        isDisabled = info.isDisabled;
     }
 
     public void setIcon(Bitmap b) {
@@ -214,22 +216,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);
-            }
+        }
+        if (iconResource != null) {
+            values.put(LauncherSettings.BaseLauncherColumns.ICON_PACKAGE,
+                    iconResource.packageName);
+            values.put(LauncherSettings.BaseLauncherColumns.ICON_RESOURCE,
+                    iconResource.resourceName);
         }
     }
 
@@ -238,16 +232,7 @@
         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);
-        }
+                + " user=" + user + ")";
     }
 
     public ComponentName getTargetComponent() {
@@ -282,11 +267,15 @@
         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;
     }
+
+    @Override
+    public boolean isDisabled() {
+        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 87c9262..53522fb 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -19,6 +19,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;
@@ -47,7 +48,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.PowerManager;
-import android.os.Process;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.TextUtils;
@@ -62,10 +62,13 @@
 
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.config.ProviderConfig;
 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.Locale;
 import java.util.Set;
@@ -117,6 +120,9 @@
     public static final boolean ATLEAST_JB_MR2 =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2;
 
+    // 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();
     private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
@@ -129,29 +135,52 @@
             CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
             TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
 
-    // To turn on these properties, type
-    // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
-    private static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate";
-    private static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY);
-
     public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
 
     public static boolean isPropertyEnabled(String propertyName) {
         return Log.isLoggable(propertyName, Log.VERBOSE);
     }
 
-    public static boolean isAllowRotationPrefEnabled(Context context, boolean multiProcess) {
-        SharedPreferences sharedPrefs = context.getSharedPreferences(
-                LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE | (multiProcess ?
-                        Context.MODE_MULTI_PROCESS : 0));
-        boolean allowRotationPref = sharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false);
-        return sForceEnableRotation || allowRotationPref;
+    public static boolean isAllowRotationPrefEnabled(Context context) {
+        return getPrefs(context).getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
+                getAllowRotationDefaultValue(context));
     }
 
-    public static boolean isRotationAllowedForDevice(Context context) {
-        return sForceEnableRotation || context.getResources().getBoolean(R.bool.allow_rotation);
+    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;
+                return originalSmallestWidth >= 600;
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+        return false;
     }
 
+    public static boolean isNycOrAbove() {
+        // TODO(vadimt): 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;
+        }
+    }
+
+    // TODO: Use Intent.ACTION_APPLICATION_PREFERENCES when N SDK is available.
+    public static final String ACTION_APPLICATION_PREFERENCES
+            = "android.intent.action.APPLICATION_PREFERENCES";
+
     public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) {
         byte[] data = c.getBlob(iconIndex);
         try {
@@ -205,8 +234,8 @@
     @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);
         Bitmap bitmap = createIconBitmap(icon, context, scale);
         if (Utilities.ATLEAST_LOLLIPOP && user != null
                 && !UserHandleCompat.myUserHandle().equals(user)) {
@@ -399,15 +428,6 @@
                 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);
-        }
-    }
-
     public static int[] getCenterDeltaInScreenSpace(View v0, View v1, int[] delta) {
         v0.getLocationInWindow(sLoc0);
         v1.getLocationInWindow(sLoc1);
@@ -428,11 +448,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(
@@ -575,16 +602,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.
@@ -719,13 +736,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
@@ -770,6 +780,28 @@
         return String.format(Locale.ENGLISH, "%s IN (%s)", columnName, TextUtils.join(", ", values));
     }
 
+    public static boolean isBootCompleted() {
+        try {
+            Class clazz = Class.forName("android.os.SystemProperties");
+            Method getter = clazz.getDeclaredMethod("get", String.class);
+            String value = (String) getter.invoke(null, "sys.boot_completed");
+            return "1".equals(value);
+        } catch (Exception e) {
+            Log.d(TAG, "Unable to read system properties");
+            // Assume that boot has completed
+            return true;
+        }
+    }
+
+    /**
+     * 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 boundInRange(int value, int lowerBound, int 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.
@@ -806,6 +838,29 @@
         return ATLEAST_LOLLIPOP && powerManager.isPowerSaveMode();
     }
 
+    public static boolean isWallapaperAllowed(Context context) {
+        if (isNycOrAbove()) {
+            try {
+                WallpaperManager wm = context.getSystemService(WallpaperManager.class);
+                return (Boolean) wm.getClass().getDeclaredMethod("isWallpaperSettingAllowed")
+                        .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);
+                }
+            }
+        }
+    }
+
     /**
      * 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
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..d9bd782 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;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 7b873d4..88e5251 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -28,9 +28,7 @@
 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;
@@ -40,38 +38,46 @@
 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.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.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.UninstallDropTarget.DropTargetSource;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
 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.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.logging.UserEventDispatcher;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.WallpaperUtils;
+import com.android.launcher3.util.WallpaperOffsetInterpolator;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 
@@ -88,18 +94,22 @@
 public class Workspace extends PagedView
         implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
         DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
-        Insettable, UninstallSource, AccessibilityDragSource, Stats.LaunchSourceProvider {
+        Insettable, DropTargetSource, AccessibilityDragSource, UserEventDispatcher.LaunchSourceProvider {
     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 final static long EXTRA_EMPTY_SCREEN_ID = -201;
+    private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
 
     private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200;
     private long mTouchDownTime = -1;
@@ -107,17 +117,9 @@
 
     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>();
 
@@ -137,9 +139,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;
@@ -150,7 +149,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;
 
@@ -165,12 +164,12 @@
 
     // 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;
@@ -180,28 +179,29 @@
     // in all apps or customize mode)
 
     enum State {
-        NORMAL          (SearchDropTargetBar.State.SEARCH_BAR),
-        NORMAL_HIDDEN   (SearchDropTargetBar.State.INVISIBLE_TRANSLATED),
-        SPRING_LOADED   (SearchDropTargetBar.State.DROP_TARGET),
-        OVERVIEW        (SearchDropTargetBar.State.INVISIBLE),
-        OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE);
+        NORMAL          (SearchDropTargetBar.State.SEARCH_BAR, false, false),
+        NORMAL_HIDDEN   (SearchDropTargetBar.State.INVISIBLE_TRANSLATED, false, false),
+        SPRING_LOADED   (SearchDropTargetBar.State.DROP_TARGET, false, true),
+        OVERVIEW        (SearchDropTargetBar.State.INVISIBLE, true, true),
+        OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE, true, false);
 
-        private final SearchDropTargetBar.State mBarState;
+        public final SearchDropTargetBar.State searchDropTargetBarState;
+        public final boolean shouldUpdateWidget;
+        public final boolean hasMultipleVisiblePages;
 
-        State(SearchDropTargetBar.State searchBarState) {
-            mBarState = searchBarState;
+        State(SearchDropTargetBar.State searchBarState, boolean shouldUpdateWidget,
+                boolean hasMultipleVisiblePages) {
+            searchDropTargetBarState = searchBarState;
+            this.shouldUpdateWidget = shouldUpdateWidget;
+            this.hasMultipleVisiblePages = hasMultipleVisiblePages;
         }
+    }
 
-        public SearchDropTargetBar.State getSearchDropTargetBarState() {
-            return mBarState;
-        }
-    };
-
+    @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;
@@ -211,27 +211,20 @@
 
     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 boolean mWorkspaceFadeInAdjacentScreens;
 
-    WallpaperOffsetInterpolator mWallpaperOffset;
-    @Thunk boolean mWallpaperIsLiveWallpaper;
-    @Thunk int mNumPagesForWallpaperParallax;
-    @Thunk float mLastSetWallpaperOffsetSteps = 0;
+    final WallpaperOffsetInterpolator mWallpaperOffset;
 
     @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;
@@ -276,21 +269,18 @@
     LauncherOverlay mLauncherOverlay;
     boolean mScrollInteractionBegan;
     boolean mStartedSendingScrollEvents;
-    boolean mShouldSendPageSettled;
-    int mLastOverlaySroll = 0;
+    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;
 
     // Handles workspace state transitions
     private WorkspaceStateTransitionAnimation mStateTransitionAnimation;
 
     private AccessibilityDelegate mPagesAccessibilityDelegate;
 
-    private final Runnable mBindPages = new Runnable() {
-        @Override
-        public void run() {
-            mLauncher.getModel().bindRemainingSynchronousPages();
-        }
-    };
-
     /**
      * Used to inflate the Workspace from XML.
      *
@@ -321,14 +311,12 @@
         mFadeInAdjacentScreens = false;
         mWallpaperManager = WallpaperManager.getInstance(context);
 
-        TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.Workspace, defStyle, 0);
+        mWallpaperOffset = new WallpaperOffsetInterpolator(this);
+
         mSpringLoadedShrinkFactor =
                 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
         mOverviewModeShrinkFactor =
                 res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f;
-        mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
-        a.recycle();
 
         setOnHierarchyChangeListener(this);
         setHapticFeedbackEnabled(false);
@@ -381,12 +369,11 @@
     }
 
     @Override
-    public void onDragStart(final DragSource source, Object info, int dragAction) {
+    public void onDragStart(final DragSource source, ItemInfo info, int dragAction) {
         if (ENFORCE_DRAG_EVENT_ORDER) {
             enfoceDragParity("onDragStart", 0, 0);
         }
 
-        mIsDragOccuring = true;
         updateChildrenLayersEnabled(false);
         mLauncher.lockScreenOrientation();
         mLauncher.onInteractionBegin();
@@ -417,7 +404,6 @@
             removeExtraEmptyScreen(true, mDragSourceInternal != null);
         }
 
-        mIsDragOccuring = false;
         updateChildrenLayersEnabled(false);
         mLauncher.unlockScreenOrientation(false);
 
@@ -432,7 +418,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();
@@ -444,10 +430,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
@@ -456,6 +438,10 @@
         setEdgeGlowColor(getResources().getColor(R.color.workspace_edge_effect_color));
     }
 
+    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();
@@ -588,9 +574,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;
@@ -616,9 +599,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;
@@ -697,7 +677,6 @@
     private void convertFinalScreenToEmptyScreenIfNecessary() {
         if (mLauncher.isWorkspaceLoading()) {
             // Invalid and dangerous operation if workspace is loading
-            Launcher.addDumpLog(TAG, "    - workspace loading, skip", true);
             return;
         }
 
@@ -730,7 +709,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;
         }
 
@@ -816,7 +794,6 @@
     public long commitExtraEmptyScreen() {
         if (mLauncher.isWorkspaceLoading()) {
             // Invalid and dangerous operation if workspace is loading
-            Launcher.addDumpLog(TAG, "    - workspace loading, skip", true);
             return -1;
         }
 
@@ -825,7 +802,9 @@
         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);
 
@@ -841,8 +820,7 @@
     }
 
     public CellLayout getScreenWithId(long screenId) {
-        CellLayout layout = mWorkspaceScreens.get(screenId);
-        return layout;
+        return mWorkspaceScreens.get(screenId);
     }
 
     public long getIdForScreen(CellLayout layout) {
@@ -872,7 +850,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;
         }
 
@@ -941,7 +918,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);
     }
@@ -1032,7 +1009,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)) {
@@ -1220,7 +1197,7 @@
             }
         }
 
-        if (mDelayedResizeRunnable != null) {
+        if (mDelayedResizeRunnable != null && !mIsSwitchingState) {
             mDelayedResizeRunnable.run();
             mDelayedResizeRunnable = null;
         }
@@ -1233,11 +1210,6 @@
             stripEmptyScreens();
             mStripScreensOnPageStopMoving = false;
         }
-
-        if (mShouldSendPageSettled) {
-            mLauncherOverlay.onScrollSettled();
-            mShouldSendPageSettled = false;
-        }
     }
 
     protected void onScrollInteractionBegin() {
@@ -1256,6 +1228,47 @@
 
     public void setLauncherOverlay(LauncherOverlay overlay) {
         mLauncherOverlay = overlay;
+        // A new overlay has been set. Reset event tracking
+        mStartedSendingScrollEvents = false;
+        onOverlayScrollChanged(0);
+    }
+
+    @Override
+    protected int getUnboundedScrollX() {
+        if (isScrollingOverlay()) {
+            return mUnboundedScrollX;
+        }
+
+        return super.getUnboundedScrollX();
+    }
+
+    private boolean isScrollingOverlay() {
+        return mLauncherOverlay != null &&
+                ((mIsRtl && mUnboundedScrollX > mMaxScrollX) || (!mIsRtl && mUnboundedScrollX < 0));
+    }
+
+    @Override
+    protected void snapToDestination() {
+        // If we're overscrolling the overlay, we make sure to immediately reset the PagedView
+        // to it's baseline position instead of letting the overscroll settle. The overlay handles
+        // it's own settling, and every gesture to the overlay should be self-contained and start
+        // from 0, so we zero it out here.
+        if (isScrollingOverlay()) {
+            int finalScroll = mIsRtl ? mMaxScrollX : 0;
+
+            // We reset mWasInOverscroll so that PagedView doesn't zero out the overscroll
+            // interaction when we call scrollTo.
+            mWasInOverscroll = false;
+            scrollTo(finalScroll, getScrollY());
+        } else {
+            super.snapToDestination();
+        }
+    }
+
+    @Override
+    public void scrollTo(int x, int y) {
+        mUnboundedScrollX = x;
+        super.scrollTo(x, y);
     }
 
     @Override
@@ -1273,15 +1286,10 @@
             if (!mStartedSendingScrollEvents && mScrollInteractionBegan) {
                 mStartedSendingScrollEvents = true;
                 mLauncherOverlay.onScrollInteractionBegin();
-                mShouldSendPageSettled = true;
             }
-            int screenSize = getViewportWidth();
-            float f = (amount / screenSize);
 
-            int progress = (int) Math.abs((f * 100));
-
-            mLastOverlaySroll = progress;
-            mLauncherOverlay.onScrollChange(progress, mIsRtl);
+            mLastOverlaySroll = Math.abs(amount / getViewportWidth());
+            mLauncherOverlay.onScrollChange(mLastOverlaySroll, mIsRtl);
         } else if (shouldOverScroll) {
             dampedOverScroll(amount);
         }
@@ -1291,6 +1299,62 @@
         }
     }
 
+    private final Interpolator mAlphaInterpolator = new DecelerateInterpolator(3f);
+
+    /**
+     * The overlay scroll is being controlled locally, just update our overlay effect
+     */
+    public void onOverlayScrollChanged(float scroll) {
+        float offset = 0f;
+        float slip = 0f;
+
+        scroll = Math.max(scroll - offset, 0);
+        scroll = Math.min(1, scroll / (1 - offset));
+
+        float alpha = 1 - mAlphaInterpolator.getInterpolation(scroll);
+        float transX = mLauncher.getDragLayer().getMeasuredWidth() * scroll;
+        transX *= 1 - slip;
+
+        if (mIsRtl) {
+            transX = -transX;
+        }
+        mOverlayTranslation = transX;
+
+        // 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);
+
+        // When the animation finishes, reset all pages, just in case we missed a page.
+        if (transX == 0) {
+            for (int i = getChildCount() - 1; i >= 0; i--) {
+                setTranslationAndAlpha(getChildAt(i), 0, alpha);
+            }
+        }
+    }
+
+    @Override
+    protected Matrix getPageShiftMatrix() {
+        if (Float.compare(mOverlayTranslation, 0) != 0) {
+            // The pages are translated by mOverlayTranslation. incorporate that in the
+            // visible page calculation by shifting everything back by that same amount.
+            mTempMatrix.set(getMatrix());
+            mTempMatrix.postTranslate(-mOverlayTranslation, 0);
+            return mTempMatrix;
+        }
+        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);
@@ -1321,17 +1385,17 @@
     }
 
     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);
+        });
     }
 
     protected void snapToPage(int whichPage, Runnable r) {
@@ -1354,197 +1418,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()) {
@@ -1566,18 +1449,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() &&
@@ -1609,6 +1480,7 @@
             setOnClickListener(mLauncher);
         }
         mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable);
+        mLauncher.getAppInfoDropTargetBar().enableAccessibleDrag(enable);
         mLauncher.getHotseat().getLayout()
             .enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
     }
@@ -1680,13 +1552,12 @@
         if (!am.isTouchExplorationEnabled()) {
             return null;
         }
-        OnClickListener listener = new OnClickListener() {
+        return new OnClickListener() {
             @Override
             public void onClick(View arg0) {
                 mLauncher.showOverviewMode(true);
             }
         };
-        return listener;
     }
 
     @Override
@@ -1698,14 +1569,15 @@
 
     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() {
@@ -1723,10 +1595,7 @@
         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
@@ -1739,14 +1608,6 @@
     }
 
     @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-
-        // Call back to LauncherModel to finish binding after the first draw
-        post(mBindPages);
-    }
-
-    @Override
     protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
         if (!mLauncher.isAppsViewVisible()) {
             final Folder openFolder = getOpenFolder();
@@ -1872,8 +1733,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.boundInRange(getCurrentPage() - 1, numCustomPages(), range[1]);
+            range[1] = Utilities.boundInRange(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();
@@ -1937,6 +1808,18 @@
     }
 
     public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
+        // Find a page that has enough space to place this widget (after rearranging/resizing).
+        // 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 currentPageInOverview = getPageNearestToCenterOfScreen();
+        for (int pageIndex = currentPageInOverview; pageIndex < getPageCount(); pageIndex++) {
+            CellLayout page = (CellLayout) getPageAt(pageIndex);
+            if (page.hasReorderSolution(info)) {
+                setCurrentPage(pageIndex);
+                break;
+            }
+        }
+
         int[] size = estimateItemSize(info, false);
 
         // The outline is used to visualize where the item will land if dropped
@@ -1958,7 +1841,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() {
@@ -1992,6 +1875,10 @@
         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()));
@@ -2007,20 +1894,41 @@
         return -workspaceOffsetTopEdge + overviewOffsetTopEdge;
     }
 
+    int getSpringLoadedTranslationY() {
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        Rect workspacePadding = grid.getWorkspacePadding(Utilities.isRtl(getResources()));
+        int scaledHeight = (int) (mSpringLoadedShrinkFactor * getNormalChildHeight());
+        int workspaceTop = mInsets.top + workspacePadding.top;
+        int workspaceBottom = getViewportHeight() - mInsets.bottom - workspacePadding.bottom;
+        int workspaceHeight = workspaceBottom - workspaceTop;
+        // Center the spring-loaded pages by translating it up by half of the reduced height.
+        return -(workspaceHeight - scaledHeight) / 2;
+    }
+
+    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);
+                toState, animated, layerViews);
 
+        boolean shouldNotifyWidgetChange = !mState.shouldUpdateWidget
+                && toState.shouldUpdateWidget;
         // Update the current state
         mState = toState;
         updateAccessibilityFlags();
 
+        if (shouldNotifyWidgetChange) {
+            mLauncher.notifyWidgetProvidersChanged();
+        }
+
         return workspaceAnim;
     }
 
@@ -2068,12 +1976,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();
@@ -2093,10 +2004,15 @@
         mIsSwitchingState = false;
         updateChildrenLayersEnabled(false);
         showCustomContentIfNecessary();
+        mForceDrawAdjacentPages = false;
     }
 
     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);
         }
@@ -2139,19 +2055,17 @@
      * @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 {
+            final Rect clipRect = sTempRect;
+            v.getDrawingRect(clipRect);
+
+            boolean textVisible = false;
             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).
@@ -2329,7 +2243,8 @@
             icon.clearPressedBackground();
         }
 
-        if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
+        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();
@@ -2340,11 +2255,16 @@
             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,
+                (ItemInfo) dragObject, DragController.DRAG_ACTION_MOVE, dragVisualizeOffset,
+                dragRect, scale, accessible);
         dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
 
         b.recycle();
+
+        if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
+            mLauncher.enterSpringLoadedDragMode();
+        }
     }
 
     public void beginExternalDragShared(View child, DragSource source) {
@@ -2377,7 +2297,8 @@
         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)) {
+        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();
@@ -2385,12 +2306,17 @@
         }
 
         // Start the drag
-        DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
-                DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, false);
+        DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source,
+                (ItemInfo) dragObject, DragController.DRAG_ACTION_MOVE, dragVisualizeOffset,
+                dragRect, scale, false);
         dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
 
         // Recycle temporary bitmaps
         tmpB.recycle();
+
+        if (!FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND) {
+            mLauncher.enterSpringLoadedDragMode();
+        }
     }
 
     public boolean transitionStateShouldAllowDrop() {
@@ -2417,7 +2343,7 @@
             if (mLauncher.isHotseatLayout(dropTargetLayout)) {
                 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
             } else {
-                mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter, null);
+                mapPointFromSelfToChild(dropTargetLayout, mDragViewVisualCenter);
             }
 
             int spanX = 1;
@@ -2427,9 +2353,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;
@@ -2444,12 +2369,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;
             }
@@ -2518,14 +2443,14 @@
         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)) {
@@ -2584,6 +2509,10 @@
             // 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 {
@@ -2630,11 +2559,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],
@@ -2643,7 +2572,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);
@@ -2677,7 +2605,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) {
@@ -2715,7 +2643,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],
@@ -2740,21 +2668,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);
                                     }
                                 }
-                            });
+                            };
                         }
                     }
 
@@ -2771,7 +2692,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() {
@@ -2779,9 +2699,6 @@
                 public void run() {
                     mAnimatingViewIntoPlace = false;
                     updateChildrenLayersEnabled(false);
-                    if (finalResizeRunnable != null) {
-                        finalResizeRunnable.run();
-                    }
                 }
             };
             mAnimatingViewIntoPlace = true;
@@ -2795,9 +2712,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);
                 }
@@ -2831,19 +2746,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) {
@@ -2858,50 +2760,11 @@
         setCurrentDropLayout(layout);
         setCurrentDragOverlappingLayout(layout);
 
-        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) {
@@ -2978,7 +2841,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) {
@@ -3012,9 +2881,8 @@
     }
 
     private void cleanupFolderCreation() {
-        if (mDragFolderRingAnimator != null) {
-            mDragFolderRingAnimator.animateToNaturalState();
-            mDragFolderRingAnimator = null;
+        if (mFolderCreateBg != null) {
+            mFolderCreateBg.animateToRest();
         }
         mFolderCreationAlarm.setOnAlarmListener(null);
         mFolderCreationAlarm.cancelAlarm();
@@ -3040,41 +2908,27 @@
     *
     * 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);
+       return mLauncher.getDeviceProfile().isInHotseatRect(mTempXY[0], mTempXY[1]);
    }
 
    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];
    }
 
    /*
@@ -3120,10 +2974,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()) {
@@ -3165,11 +3016,10 @@
         // 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;
@@ -3183,7 +3033,7 @@
         // Identify whether we have dragged over a side page
         if (workspaceInModalState()) {
             if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
-                if (isPointInSelfOverHotseat(d.x, d.y, r)) {
+                if (isPointInSelfOverHotseat(d.x, d.y)) {
                     layout = mLauncher.getHotseat().getLayout();
                 }
             }
@@ -3206,7 +3056,7 @@
         } 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)) {
+                if (isPointInSelfOverHotseat(d.x, d.y)) {
                     layout = mLauncher.getHotseat().getLayout();
                 }
             }
@@ -3225,7 +3075,7 @@
             if (mLauncher.isHotseatLayout(mDragTargetLayout)) {
                 mapPointFromSelfToHotseatLayout(mLauncher.getHotseat(), mDragViewVisualCenter);
             } else {
-                mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
+                mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter);
             }
 
             int minSpanX = item.spanX;
@@ -3286,7 +3136,7 @@
         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()) {
@@ -3337,22 +3187,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);
         }
@@ -3407,24 +3261,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.
@@ -3432,7 +3268,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
@@ -3442,7 +3278,7 @@
             }
         };
 
-        ItemInfo info = (ItemInfo) dragInfo;
+        ItemInfo info = dragInfo;
         int spanX = info.spanX;
         int spanY = info.spanY;
         if (mDragInfo != null) {
@@ -3469,14 +3305,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;
@@ -3721,6 +3557,10 @@
         }
     }
 
+    public WorkspaceStateTransitionAnimation getStateTransitionAnimation() {
+        return mStateTransitionAnimation;
+    }
+
     /**
      * Return the current {@link CellLayout}, correctly picking the destination
      * screen while a scroll is in progress.
@@ -3789,7 +3629,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. ");
             };
@@ -3800,6 +3640,13 @@
         }
         mDragOutline = 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;
+        }
     }
 
     /**
@@ -3809,7 +3656,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
@@ -3828,7 +3675,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) {
@@ -3836,70 +3683,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;
@@ -3912,7 +3695,7 @@
 
     @Override
     public boolean supportsAppInfoDropTarget() {
-        return false;
+        return !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND;
     }
 
     @Override
@@ -4078,8 +3861,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());
@@ -4090,22 +3872,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;
             }
         });
@@ -4115,7 +3886,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;
             }
         });
@@ -4125,7 +3896,7 @@
         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;
             }
@@ -4136,8 +3907,8 @@
         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;
                 }
@@ -4150,7 +3921,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);
                 }
@@ -4160,41 +3931,10 @@
         });
     }
 
-    public void disableShortcutsByPackageName(final ArrayList<String> packages,
-            final UserHandleCompat user, final int reason) {
-        final HashSet<String> packageNames = new HashSet<String>();
-        packageNames.addAll(packages);
-
-        mapOverItems(MAP_RECURSE, new ItemOperator() {
-            @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
-                if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
-                    ShortcutInfo shortcutInfo = (ShortcutInfo) info;
-                    ComponentName cn = shortcutInfo.getTargetComponent();
-                    if (user.equals(shortcutInfo.user) && cn != null
-                            && packageNames.contains(cn.getPackageName())) {
-                        shortcutInfo.isDisabled |= reason;
-                        BubbleTextView shortcut = (BubbleTextView) v;
-                        shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache);
-
-                        if (parent != null) {
-                            parent.invalidate();
-                        }
-                    }
-                }
-                // process all the shortcuts
-                return false;
-            }
-        });
-    }
-
     // 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 ArrayList<String> packages, final UserHandleCompat user) {
-        final HashSet<String> packageNames = new HashSet<String>();
-        packageNames.addAll(packages);
-
+    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>();
@@ -4277,7 +4017,7 @@
             for (FolderInfo folder : folderAppsToRemove.keySet()) {
                 ArrayList<ShortcutInfo> appsToRemove = folderAppsToRemove.get(folder);
                 for (ShortcutInfo info : appsToRemove) {
-                    folder.remove(info);
+                    folder.remove(info, false);
                 }
             }
 
@@ -4301,16 +4041,15 @@
         stripEmptyScreens();
     }
 
-    interface ItemOperator {
+    public interface ItemOperator {
         /**
          * Process the next itemInfo, possibly with side-effect on {@link ItemOperator#value}.
          *
          * @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);
     }
 
     /**
@@ -4337,12 +4076,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;
                     }
                 }
@@ -4351,10 +4090,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;
@@ -4364,10 +4112,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;
@@ -4376,7 +4132,7 @@
     }
 
     public void removeAbandonedPromise(String packageName, UserHandleCompat user) {
-        ArrayList<String> packages = new ArrayList<String>(1);
+        HashSet<String> packages = new HashSet<>(1);
         packages.add(packageName);
         LauncherModel.deletePackageFromDatabase(mLauncher, packageName, user);
         removeItemsByPackageName(packages, user);
@@ -4385,7 +4141,7 @@
     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);
@@ -4400,13 +4156,22 @@
         });
     }
 
-    void widgetsRestored(ArrayList<LauncherAppWidgetInfo> changedInfo) {
+    public void widgetsRestored(ArrayList<LauncherAppWidgetInfo> changedInfo) {
         if (!changedInfo.isEmpty()) {
             DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
                     mLauncher.getAppWidgetHost());
-            if (LauncherModel.getProviderInfo(getContext(),
-                    changedInfo.get(0).providerName,
-                    changedInfo.get(0).user) != null) {
+
+            LauncherAppWidgetInfo item = changedInfo.get(0);
+            final AppWidgetProviderInfo widgetInfo;
+            if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+                widgetInfo = AppWidgetManagerCompat
+                        .getInstance(mLauncher).findProvider(item.providerName, item.user);
+            } else {
+                widgetInfo = AppWidgetManagerCompat.getInstance(mLauncher)
+                        .getAppWidgetInfo(item.appWidgetId);
+            }
+
+            if (widgetInfo != null) {
                 // Re-inflate the widgets which have changed status
                 widgetRefresh.run();
             } else {
@@ -4437,7 +4202,7 @@
     }
 
     void moveToDefaultScreen(boolean animate) {
-        moveToScreen(mDefaultPage, animate);
+        moveToScreen(getDefaultPage(), animate);
     }
 
     void moveToCustomContentScreen(boolean animate) {
@@ -4493,19 +4258,21 @@
             }
             nScreens--;
         }
-        return String.format(getContext().getString(R.string.workspace_scroll_format),
+        if (nScreens == 0) {
+            // When the workspace is not loaded, we do not know how many screen will be bound.
+            return getContext().getString(R.string.all_apps_home_button_label);
+        }
+        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.itemType = LauncherLogProto.APP_ICON;
+        target.gridX = info.cellX;
+        target.gridY = info.cellY;
+        target.pageIndex = getCurrentPage();
+        targetParent.containerType = LauncherLogProto.WORKSPACE;
     }
 
     /**
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 54f63bb..c0eb7ed 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -30,6 +30,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.DecelerateInterpolator;
 
+import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.util.Thunk;
 
 import java.util.HashMap;
@@ -174,7 +175,6 @@
 
     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;
@@ -198,6 +198,7 @@
     @Thunk int mAllAppsTransitionTime;
     @Thunk int mOverviewTransitionTime;
     @Thunk int mOverlayTransitionTime;
+    @Thunk int mSpringLoadedTransitionTime;
     @Thunk boolean mWorkspaceFadeInAdjacentScreens;
 
     public WorkspaceStateTransitionAnimation(Launcher launcher, Workspace workspace) {
@@ -209,6 +210,7 @@
         mAllAppsTransitionTime = res.getInteger(R.integer.config_allAppsTransitionTime);
         mOverviewTransitionTime = res.getInteger(R.integer.config_overviewTransitionTime);
         mOverlayTransitionTime = res.getInteger(R.integer.config_overlayTransitionTime);
+        mSpringLoadedTransitionTime = mOverlayTransitionTime / 2;
         mSpringLoadedShrinkFactor =
                 res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100f;
         mOverviewModeShrinkFactor =
@@ -217,14 +219,18 @@
         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;
@@ -255,6 +261,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,7 +272,7 @@
     /**
      * 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
@@ -278,11 +287,16 @@
         // 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) ? 1f : 0f;
+        float finalPageIndicatorAlpha = states.stateIsNormal ? 1f : 0f;
         float finalOverviewPanelAlpha = states.stateIsOverview ? 1f : 0f;
-        float finalWorkspaceTranslationY = states.stateIsOverview || states.stateIsOverviewHidden ?
-                mWorkspace.getOverviewModeTranslationY() : 0;
+
+        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,11 +317,7 @@
             }
         }
 
-        if (toPage == SCROLL_TO_CURRENT_PAGE) {
-            toPage = mWorkspace.getPageNearestToCenterOfScreen();
-        }
-        mWorkspace.snapToPage(toPage, duration, mZoomInInterpolator);
-
+        int toPage = mWorkspace.getPageNearestToCenterOfScreen();
         for (int i = 0; i < childCount; i++) {
             final CellLayout cl = (CellLayout) mWorkspace.getChildAt(i);
             boolean isCurrentPage = (i == toPage);
@@ -379,7 +389,6 @@
                             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);
@@ -389,7 +398,7 @@
             Animator pageIndicatorAlpha;
             if (pageIndicator != null) {
                 pageIndicatorAlpha = new LauncherViewPropertyAnimator(pageIndicator)
-                        .alpha(finalHotseatAndPageIndicatorAlpha).withLayer();
+                        .alpha(finalPageIndicatorAlpha).withLayer();
                 pageIndicatorAlpha.addListener(new AlphaUpdateListener(pageIndicator,
                         accessibilityEnabled));
             } else {
@@ -398,7 +407,7 @@
             }
 
             LauncherViewPropertyAnimator hotseatAlpha = new LauncherViewPropertyAnimator(hotseat)
-                    .alpha(finalHotseatAndPageIndicatorAlpha);
+                    .alpha(finalHotseatAlpha);
             hotseatAlpha.addListener(new AlphaUpdateListener(hotseat, accessibilityEnabled));
 
             LauncherViewPropertyAnimator overviewPanelAlpha =
@@ -452,10 +461,10 @@
         } else {
             overviewPanel.setAlpha(finalOverviewPanelAlpha);
             AlphaUpdateListener.updateVisibility(overviewPanel, accessibilityEnabled);
-            hotseat.setAlpha(finalHotseatAndPageIndicatorAlpha);
+            hotseat.setAlpha(finalHotseatAlpha);
             AlphaUpdateListener.updateVisibility(hotseat, accessibilityEnabled);
             if (pageIndicator != null) {
-                pageIndicator.setAlpha(finalHotseatAndPageIndicatorAlpha);
+                pageIndicator.setAlpha(finalPageIndicatorAlpha);
                 AlphaUpdateListener.updateVisibility(pageIndicator, accessibilityEnabled);
             }
             mWorkspace.updateCustomContentVisibility();
@@ -488,8 +497,7 @@
             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/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..aa6e08e 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -20,9 +20,8 @@
 import com.android.launcher3.AppWidgetResizeFrame;
 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.folder.Folder;
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InfoDropTarget;
 import com.android.launcher3.ItemInfo;
@@ -36,6 +35,7 @@
 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.util.Thunk;
 
 import java.util.ArrayList;
@@ -75,11 +75,11 @@
         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,
@@ -96,13 +96,13 @@
         if (!(host.getTag() instanceof ItemInfo)) return;
         ItemInfo item = (ItemInfo) host.getTag();
 
-        if (DeleteDropTarget.supportsDrop(item)) {
+        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));
         }
 
@@ -137,7 +137,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 +174,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);
@@ -372,7 +372,7 @@
 
 
     @Override
-    public void onDragStart(DragSource source, Object info, int dragAction) {
+    public void onDragStart(DragSource source, ItemInfo info, int dragAction) {
         // No-op
     }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index c12f645..c9bd02c 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -40,7 +40,7 @@
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.ExtendedEditText;
-import com.android.launcher3.Folder;
+import com.android.launcher3.folder.Folder;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherTransitionable;
@@ -230,8 +230,6 @@
         mSearchBarController = searchController;
         mSearchBarController.initialize(mApps, mSearchInput, mLauncher, this);
         mAdapter.setSearchController(mSearchBarController);
-
-        updateBackgroundAndPaddings();
     }
 
     /**
@@ -311,19 +309,17 @@
         mAppsRecyclerView.setPremeasuredIconHeights(predIcon.getMeasuredHeight(),
                 icon.getMeasuredHeight());
 
-        updateBackgroundAndPaddings();
+        updatePaddingsAndMargins();
     }
 
     @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);
+        mContentBounds.set(mHorizontalPadding, 0,
+                MeasureSpec.getSize(widthMeasureSpec) - mHorizontalPadding,
+                MeasureSpec.getSize(heightMeasureSpec));
 
         // Update the number of items in the grid before we measure the view
         // TODO: mSectionNamesMargin is currently 0, but also account for it,
@@ -365,8 +361,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() {
+        Rect bgPadding = new Rect();
+        getRevealView().getBackground().getPadding(bgPadding);
+
         mAppsRecyclerView.updateBackgroundPadding(bgPadding);
         mAdapter.updateBackgroundPadding(bgPadding);
         mElevationController.updateBackgroundPadding(bgPadding);
@@ -377,16 +375,16 @@
         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,
+                    topBottomPadding, bgPadding.right + startInset, topBottomPadding);
         } else {
-            mAppsRecyclerView.setPadding(padding.left + startInset, topBottomPadding,
-                    padding.right + maxScrollBarWidth, topBottomPadding);
+            mAppsRecyclerView.setPadding(bgPadding.left + startInset, topBottomPadding,
+                    bgPadding.right + maxScrollBarWidth, topBottomPadding);
         }
 
         MarginLayoutParams lp = (MarginLayoutParams) mSearchContainer.getLayoutParams();
-        lp.leftMargin = padding.left;
-        lp.rightMargin = padding.right;
+        lp.leftMargin = bgPadding.left;
+        lp.rightMargin = bgPadding.right;
         mSearchContainer.setLayoutParams(lp);
     }
 
@@ -501,7 +499,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);
@@ -516,7 +514,8 @@
     }
 
     @Override
-    public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
+    public void onLauncherTransitionPrepare(Launcher l, boolean animated,
+            boolean multiplePagesVisible) {
         // Do nothing
     }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index d41224a..115db9d 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -345,6 +345,7 @@
 
     private final int mPredictionBarDividerOffset;
     private int mAppsPerRow;
+
     private BindViewCallback mBindViewCallback;
     private AllAppsSearchBarController mSearchController;
 
@@ -417,10 +418,9 @@
      */
     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);
+        mEmptySearchMessage = res.getString(R.string.all_apps_no_search_results, query);
         if (mMarketAppName != null) {
-            mMarketSearchMessage = String.format(res.getString(R.string.all_apps_search_market_message),
+            mMarketSearchMessage = res.getString(R.string.all_apps_search_market_message,
                     mMarketAppName);
             mMarketSearchIntent = mSearchController.createMarketSearchIntent(query);
         }
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 2b3d061..8d5ade3 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -19,7 +19,6 @@
 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.view.View;
@@ -27,17 +26,19 @@
 import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.ItemInfo;
 import com.android.launcher3.R;
-import com.android.launcher3.Stats;
 import com.android.launcher3.Utilities;
-
+import com.android.launcher3.logging.UserEventDispatcher.LaunchSourceProvider;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 import java.util.List;
 
 /**
  * A RecyclerView with custom fast scroll support for the all apps view.
  */
 public class AllAppsRecyclerView extends BaseRecyclerView
-        implements Stats.LaunchSourceProvider {
+        implements LaunchSourceProvider {
 
     private AlphabeticalAppsList mApps;
     private AllAppsFastScrollHelper mFastScrollHelper;
@@ -165,11 +166,9 @@
     }
 
     @Override
-    public void fillInLaunchSourceData(View v, Bundle sourceData) {
-        sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_ALL_APPS);
+    public void fillInLaunchSourceData(View v, ItemInfo info, Target target, Target targetParent) {
         if (mApps.hasFilter()) {
-            sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER,
-                    Stats.SUB_CONTAINER_ALL_APPS_SEARCH);
+            targetParent.containerType = LauncherLogProto.SEARCHRESULT;
         } else {
             if (v instanceof BubbleTextView) {
                 BubbleTextView icon = (BubbleTextView) v;
@@ -178,14 +177,13 @@
                     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);
+                        targetParent.containerType = LauncherLogProto.PREDICTION;
                         return;
                     }
                 }
             }
-            sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER,
-                    Stats.SUB_CONTAINER_ALL_APPS_A_Z);
+
+            targetParent.containerType = LauncherLogProto.ALLAPPS;
         }
     }
 
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
index 14e2a18..09a7d59 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
@@ -17,17 +17,15 @@
 
 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;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
 
 /**
  * A container for RecyclerView to allow for the click shadow view to be shown behind an icon that
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
index 39d6dd5..a4bea8d 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
@@ -175,6 +175,7 @@
         /**
          * Called when the bounds of the search bar has changed.
          */
+        @Deprecated
         void onBoundsChanged(Rect newBounds);
 
         /**
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 26e9231..9d2fe54 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;
 
@@ -186,7 +186,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;
@@ -215,7 +215,7 @@
     /**
      * Sets the adapter to notify when this dataset changes.
      */
-    public void setAdapter(RecyclerView.Adapter adapter) {
+    public void setAdapter(AllAppsGridAdapter adapter) {
         mAdapter = adapter;
     }
 
@@ -422,7 +422,7 @@
                 if (info != null) {
                     mPredictedApps.add(info);
                 } else {
-                    if (LauncherAppState.isDogfoodBuild()) {
+                    if (ProviderConfig.IS_DOGFOOD_BUILD) {
                         Log.e(TAG, "Predicted app not found: " + ck.flattenToString(mLauncher));
                     }
                 }
diff --git a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
index ec1fb66..463278a 100644
--- a/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
+++ b/src/com/android/launcher3/compat/AlphabeticIndexCompat.java
@@ -1,6 +1,7 @@
 package com.android.launcher3.compat;
 
 import android.content.Context;
+
 import com.android.launcher3.Utilities;
 
 import java.lang.reflect.Constructor;
@@ -62,6 +63,7 @@
     private boolean mHasValidAlphabeticIndex;
     private String mDefaultMiscLabel;
 
+    @SuppressWarnings({"unchecked", "rawtypes"})
     public AlphabeticIndexCompat(Context context) {
         super();
         try {
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index f0221bc..811cacf 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -20,6 +20,7 @@
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
@@ -28,7 +29,9 @@
 import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.ComponentKey;
 
+import java.util.HashMap;
 import java.util.List;
 
 public abstract class AppWidgetManagerCompat {
@@ -62,6 +65,11 @@
         return mAppWidgetManager.getAppWidgetInfo(appWidgetId);
     }
 
+    public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo(int appWidgetId) {
+        AppWidgetProviderInfo info = getAppWidgetInfo(appWidgetId);
+        return info == null ? null : LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+    }
+
     public abstract List<AppWidgetProviderInfo> getAllProviders();
 
     public abstract String loadLabel(LauncherAppWidgetProviderInfo info);
@@ -81,4 +89,8 @@
     public abstract Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap,
             int imageWidth, int imageHeight);
 
+    public abstract LauncherAppWidgetProviderInfo findProvider(
+            ComponentName provider, UserHandleCompat user);
+
+    public abstract HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap();
 }
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
index e9d2510..de9414e 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
@@ -21,6 +21,7 @@
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -31,7 +32,9 @@
 import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.ComponentKey;
 
+import java.util.HashMap;
 import java.util.List;
 
 class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat {
@@ -91,4 +94,25 @@
             int imageWidth, int imageHeight) {
         return bitmap;
     }
+
+    @Override
+    public LauncherAppWidgetProviderInfo findProvider(
+            ComponentName provider, UserHandleCompat user) {
+        for (AppWidgetProviderInfo info : mAppWidgetManager.getInstalledProviders()) {
+            if (info.provider.equals(provider)) {
+                return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap() {
+        HashMap<ComponentKey, AppWidgetProviderInfo> result = new HashMap<>();
+        UserHandleCompat user = UserHandleCompat.myUserHandle();
+        for (AppWidgetProviderInfo info : mAppWidgetManager.getInstalledProviders()) {
+            result.put(new ComponentKey(info.provider, user), info);
+        }
+        return result;
+    }
 }
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
index 3bc3d0d..a1570e6 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -21,6 +21,7 @@
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
@@ -40,8 +41,10 @@
 import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.util.ComponentKey;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
@@ -145,4 +148,28 @@
         c.setBitmap(null);
         return bitmap;
     }
+
+    @Override
+    public LauncherAppWidgetProviderInfo findProvider(ComponentName provider, UserHandleCompat user) {
+        for (AppWidgetProviderInfo info : mAppWidgetManager
+                .getInstalledProvidersForProfile(user.getUser())) {
+            if (info.provider.equals(provider)) {
+                return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap() {
+        HashMap<ComponentKey, AppWidgetProviderInfo> result = new HashMap<>();
+        for (UserHandle user : mUserManager.getUserProfiles()) {
+            UserHandleCompat userHandle = UserHandleCompat.fromUser(user);
+            for (AppWidgetProviderInfo info :
+                    mAppWidgetManager.getInstalledProvidersForProfile(user)) {
+                result.put(new ComponentKey(info.provider, userHandle), info);
+            }
+        }
+        return result;
+    }
 }
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java
index 15c5e69..fee0376 100644
--- a/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java
@@ -26,7 +26,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
-import android.util.Log;
 
 
 public class LauncherActivityInfoCompatV16 extends LauncherActivityInfoCompat {
@@ -52,12 +51,7 @@
     }
 
     public CharSequence getLabel() {
-        try {
-            return mResolveInfo.loadLabel(mPm);
-        } catch (SecurityException e) {
-            Log.e("LAInfoCompat", "Failed to extract app display name from resolve info", e);
-            return "";
-        }
+        return mResolveInfo.loadLabel(mPm);
     }
 
     public Drawable getIcon(int density) {
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index 95e3ba9..237a9e9 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -19,9 +19,6 @@
 import android.content.ComponentName;
 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.graphics.Rect;
 import android.os.Bundle;
 
@@ -35,6 +32,10 @@
             "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);
@@ -42,6 +43,8 @@
         void onPackageChanged(String packageName, UserHandleCompat user);
         void onPackagesAvailable(String[] packageNames, UserHandleCompat user, boolean replacing);
         void onPackagesUnavailable(String[] packageNames, UserHandleCompat user, boolean replacing);
+        void onPackagesSuspended(String[] packageNames, UserHandleCompat user);
+        void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user);
     }
 
     protected LauncherAppsCompat() {
@@ -75,13 +78,5 @@
     public abstract boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user);
     public abstract boolean isActivityEnabledForProfile(ComponentName component,
             UserHandleCompat user);
-
-    public boolean isAppEnabled(PackageManager pm, String packageName, int flags) {
-        try {
-            ApplicationInfo info = pm.getApplicationInfo(packageName, flags);
-            return info != null && info.enabled;
-        } catch (NameNotFoundException e) {
-            return false;
-        }
-    }
-}
\ No newline at end of file
+    public abstract boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user);
+}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
index 339c457..4e2fc05 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
@@ -27,11 +27,11 @@
 import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.provider.Settings;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -114,7 +114,7 @@
     }
 
     public boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user) {
-        return isAppEnabled(mPm, packageName, 0);
+        return PackageManagerHelper.isAppEnabled(mPm, packageName);
     }
 
     public boolean isActivityEnabledForProfile(ComponentName component, UserHandleCompat user) {
@@ -126,6 +126,10 @@
         }
     }
 
+    public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) {
+        return false;
+    }
+
     private void unregisterForPackageIntents() {
         mContext.unregisterReceiver(mPackageMonitor);
     }
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index fbf91b5..7270d02 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -36,7 +36,7 @@
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class LauncherAppsCompatVL extends LauncherAppsCompat {
 
-    private LauncherApps mLauncherApps;
+    protected LauncherApps mLauncherApps;
 
     private Map<OnAppsChangedCallbackCompat, WrappedCallback> mCallbacks
             = new HashMap<OnAppsChangedCallbackCompat, WrappedCallback>();
@@ -106,6 +106,10 @@
         return mLauncherApps.isActivityEnabled(component, user.getUser());
     }
 
+    public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) {
+        return false;
+    }
+
     private static class WrappedCallback extends LauncherApps.Callback {
         private LauncherAppsCompat.OnAppsChangedCallbackCompat mCallback;
 
@@ -134,6 +138,14 @@
             mCallback.onPackagesUnavailable(packageNames, UserHandleCompat.fromUser(user),
                     replacing);
         }
+
+        public void onPackagesSuspended(String[] packageNames, UserHandle user) {
+            mCallback.onPackagesSuspended(packageNames, UserHandleCompat.fromUser(user));
+        }
+
+        public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
+            mCallback.onPackagesUnsuspended(packageNames, UserHandleCompat.fromUser(user));
+        }
     }
 }
 
diff --git a/src/com/android/launcher3/compat/UserHandleCompat.java b/src/com/android/launcher3/compat/UserHandleCompat.java
index 9479908..50af21b 100644
--- a/src/com/android/launcher3/compat/UserHandleCompat.java
+++ b/src/com/android/launcher3/compat/UserHandleCompat.java
@@ -93,4 +93,14 @@
             intent.putExtra(name, mUser);
         }
     }
+
+    public static UserHandleCompat fromIntent(Intent intent) {
+        if (Utilities.ATLEAST_LOLLIPOP) {
+            UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+            if (user != null) {
+                return UserHandleCompat.fromUser(user);
+            }
+        }
+        return null;
+    }
 }
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index 6b7cba8..978f922 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -32,7 +32,9 @@
     public static UserManagerCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.ATLEAST_LOLLIPOP) {
+                if (Utilities.isNycOrAbove()) {
+                    sInstance = new UserManagerCompatVN(context.getApplicationContext());
+                } else if (Utilities.ATLEAST_LOLLIPOP) {
                     sInstance = new UserManagerCompatVL(context.getApplicationContext());
                 } else if (Utilities.ATLEAST_JB_MR1) {
                     sInstance = new UserManagerCompatV17(context.getApplicationContext());
@@ -54,4 +56,5 @@
     public abstract UserHandleCompat getUserForSerialNumber(long serialNumber);
     public abstract CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user);
     public abstract long getUserCreationTime(UserHandleCompat user);
+    public abstract boolean isQuietModeEnabled(UserHandleCompat user);
 }
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java
index fcd7555..a006efd 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java
@@ -50,4 +50,9 @@
     @Override
     public void enableAndResetCache() {
     }
+
+    @Override
+    public boolean isQuietModeEnabled(UserHandleCompat user) {
+        return false;
+    }
 }
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVN.java b/src/com/android/launcher3/compat/UserManagerCompatVN.java
new file mode 100644
index 0000000..ae41e68
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatVN.java
@@ -0,0 +1,55 @@
+/*
+ * 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.Context;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+//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";
+
+    UserManagerCompatVN(Context context) {
+        super(context);
+    }
+
+    @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;
+    }
+}
+
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 5012708..34c6663 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -18,20 +18,15 @@
 
 /**
  * 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;
-
+    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 = false;
+    public static boolean LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW = false;
 }
diff --git a/src/com/android/launcher3/config/ProviderConfig.java b/src/com/android/launcher3/config/ProviderConfig.java
index e8930d0..1d964b1 100644
--- a/src/com/android/launcher3/config/ProviderConfig.java
+++ b/src/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/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
similarity index 77%
rename from src/com/android/launcher3/DragController.java
rename to src/com/android/launcher3/dragndrop/DragController.java
index eefb287..f4470f3 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;
@@ -26,15 +26,24 @@
 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.Workspace;
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.util.Thunk;
 
@@ -44,7 +53,7 @@
 /**
  * Class for initiating a drag within a view or across multiple views.
  */
-public class DragController {
+public class DragController implements DragDriver.EventListener {
     private static final String TAG = "Launcher.DragController";
 
     /** Indicates the drag is a move.  */
@@ -61,9 +70,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,10 +84,13 @@
     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 */
+    /** Whether or not an accessible drag operation is in progress. */
     private boolean mIsAccessibleDrag;
 
     /** X coordinate of the down event. */
@@ -90,7 +102,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,7 +134,7 @@
     private int mTmpPoint[] = new int[2];
     private Rect mDragLayerRect = new Rect();
 
-    protected int mFlingToDeleteThresholdVelocity;
+    protected final int mFlingToDeleteThresholdVelocity;
     private VelocityTracker mVelocityTracker;
 
     /**
@@ -137,16 +149,18 @@
          * @param dragAction The drag action: either {@link DragController#DRAG_ACTION_MOVE}
          *        or {@link DragController#DRAG_ACTION_COPY}
          */
-        void onDragStart(DragSource source, Object info, int dragAction);
+        void onDragStart(DragSource source, ItemInfo info, int dragAction);
 
         /**
          * The drag has ended
          */
         void onDragEnd();
     }
-    
+
     /**
      * Used to create a new DragLayer from XML.
+     *
+     * @param context The application's context.
      */
     public DragController(Launcher launcher) {
         Resources r = launcher.getResources();
@@ -155,16 +169,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,13 +181,11 @@
      * @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 viewImageBounds the position of the image inside the view
      * @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,
+    public void startDrag(View v, Bitmap bmp, DragSource source, ItemInfo dragInfo,
             Rect viewImageBounds, int dragAction, float initialDragViewScale) {
         int[] loc = mCoordinatesTemp;
         mLauncher.getDragLayer().getLocationInDragLayer(v, loc);
@@ -211,7 +218,7 @@
      * @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,
+            DragSource source, ItemInfo dragInfo, int dragAction, Point dragOffset, Rect dragRegion,
             float initialDragViewScale, boolean accessible) {
         if (PROFILE_DRAWING_DURING_DRAG) {
             android.os.Debug.startMethodTracing("Launcher");
@@ -234,13 +241,14 @@
         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();
 
         final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
-                registrationY, 0, 0, b.getWidth(), b.getHeight(), initialDragViewScale);
+                registrationY, 0, 0, b.getWidth(), b.getHeight(),
+                initialDragViewScale);
 
         mDragObject.dragComplete = false;
         if (mIsAccessibleDrag) {
@@ -252,6 +260,8 @@
             mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);
             mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
             mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
+
+            mDragDriver = DragDriver.create(this, dragInfo, dragView);
         }
 
         mDragObject.dragSource = source;
@@ -266,49 +276,14 @@
 
         mLauncher.getDragLayer().performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
         dragView.show(mMotionDownX, mMotionDownY);
+        mDistanceSinceScroll = 0;
+        mLastTouch[0] = mMotionDownX;
+        mLastTouch[1] = mMotionDownY;
         handleMoveEvent(mMotionDownX, mMotionDownY);
         return dragView;
     }
 
     /**
-     * Draw the view into a bitmap.
-     */
-    Bitmap getViewBitmap(View v) {
-        v.clearFocus();
-        v.setPressed(false);
-
-        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();
-        }
-        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;
-    }
-
-    /**
      * Call this from a drag source view like this:
      *
      * <pre>
@@ -319,18 +294,18 @@
      * </pre>
      */
     public boolean dispatchKeyEvent(KeyEvent event) {
-        return mDragging;
+        return mDragDriver != null;
     }
 
     public boolean isDragging() {
-        return mDragging;
+        return mDragDriver != null || mIsAccessibleDrag;
     }
 
     /**
      * Stop dragging without dropping.
      */
     public void cancelDrag() {
-        if (mDragging) {
+        if (isDragging()) {
             if (mLastDropTarget != null) {
                 mLastDropTarget.onDragExit(mDragObject);
             }
@@ -341,7 +316,8 @@
         }
         endDrag();
     }
-    public void onAppsRemoved(final ArrayList<String> packageNames, HashSet<ComponentName> cns) {
+
+    public void onAppsRemoved(final HashSet<String> packageNames, HashSet<ComponentName> cns) {
         // Cancel the current drag if we are removing an app that we are dragging
         if (mDragObject != null) {
             Object rawDragInfo = mDragObject.dragInfo;
@@ -363,8 +339,8 @@
     }
 
     private void endDrag() {
-        if (mDragging) {
-            mDragging = false;
+        if (isDragging()) {
+            mDragDriver = null;
             mIsAccessibleDrag = false;
             clearScrollRunnable();
             boolean isDeferred = false;
@@ -415,18 +391,63 @@
         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) {
+        final int[] dragLayerPos = getClampedDragLayerPos(x, y);
+        final int dragLayerX = dragLayerPos[0];
+        final int dragLayerY = dragLayerPos[1];
+
+        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.
      */
@@ -434,8 +455,8 @@
         @SuppressWarnings("all") // suppress dead code warning
         final boolean debug = false;
         if (debug) {
-            Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " mDragging="
-                    + mDragging);
+            Log.d(Launcher.TAG, "DragController.onInterceptTouchEvent " + ev + " Dragging="
+                    + (mDragDriver != null));
         }
 
         if (mIsAccessibleDrag) {
@@ -451,41 +472,39 @@
         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;
     }    
 
@@ -520,6 +539,10 @@
         checkScrollState(x, y);
     }
 
+    public float getDistanceDragged() {
+        return mDistanceSinceScroll;
+    }
+
     public void forceTouchMove() {
         int[] dummyCoordinates = mCoordinatesTemp;
         DropTarget dropTarget = findDropTarget(mLastTouch[0], mLastTouch[1], dummyCoordinates);
@@ -556,7 +579,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);
                 }
@@ -565,7 +588,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);
                 }
@@ -579,7 +602,7 @@
      * Call this from a drag source view.
      */
     public boolean onTouchEvent(MotionEvent ev) {
-        if (!mDragging || mIsAccessibleDrag) {
+        if (mDragDriver == null || mIsAccessibleDrag) {
             return false;
         }
 
@@ -592,47 +615,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);
     }
 
     /**
@@ -642,7 +643,6 @@
     public void prepareAccessibleDrag(int x, int y) {
         mMotionDownX = x;
         mMotionDownY = y;
-        mLastDropTarget = null;
     }
 
     /**
@@ -660,7 +660,7 @@
 
         dropTarget.prepareAccessibilityDrop();
         // Perform the drop
-        drop(location[0], location[1]);
+        drop(dropTarget, location[0], location[1], null);
         endDrag();
     }
 
@@ -675,64 +675,64 @@
 
         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);
     }
 
     private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {
@@ -825,10 +825,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..7ad45f9
--- /dev/null
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -0,0 +1,251 @@
+/*
+ * 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 com.android.launcher3.AnotherWindowDropTarget;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
+
+import android.content.ClipData;
+import android.content.Intent;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.view.DragEvent;
+import android.view.MotionEvent;
+import android.view.View;
+
+/**
+ * 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 abstract 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(
+            DragController dragController, ItemInfo dragInfo, DragView dragView) {
+        if (FeatureFlags.LAUNCHER3_USE_SYSTEM_DRAG_DRIVER && Utilities.isNycOrAbove()) {
+            return new SystemDragDriver(dragController, dragInfo.getIntent(), dragView);
+        } else {
+            return new InternalDragDriver(dragController);
+        }
+    }
+
+};
+
+/**
+ * Class for driving a system (i.e. framework) drag/drop operation.
+ */
+class SystemDragDriver extends DragDriver {
+    /** Intent associated with the drag operation, or null is there no associated intent.  */
+    private final Intent mDragIntent;
+
+    private final DragView mDragView;
+    boolean mIsFrameworkDragActive = false;
+    boolean mReceivedDropEvent = false;
+    float mLastX = 0;
+    float mLastY = 0;
+
+    public SystemDragDriver(DragController dragController, Intent dragIntent, DragView dragView) {
+        super(dragController);
+        mDragIntent = dragIntent;
+        mDragView = dragView;
+    }
+
+    private static class ShadowBuilder extends View.DragShadowBuilder {
+        final DragView mDragView;
+
+        public ShadowBuilder(DragView dragView) {
+            mDragView = dragView;
+        }
+
+        @Override
+        public void onProvideShadowMetrics (Point size, Point touch) {
+            mDragView.provideDragShadowMetrics(size, touch);
+        }
+
+        @Override
+        public void onDrawShadow(Canvas canvas) {
+            mDragView.drawDragShadow(canvas);
+        }
+    };
+
+    @Override
+    public void onDragViewAnimationEnd() {
+        // Clip data for the drag operation. If there is an intent, create an intent-based ClipData,
+        // which will be passed to a global DND.
+        // If there is no intent, craft a fake ClipData and start a local DND operation; this
+        // ClipData will be ignored.
+        final ClipData dragData = mDragIntent != null ?
+                ClipData.newIntent("", mDragIntent) :
+                ClipData.newPlainText("", "");
+
+        View.DragShadowBuilder shadowBuilder = new ShadowBuilder(mDragView);
+        // TODO: DND flags are in flux, once settled, use the appropriate constant.
+        final int flagGlobal = 1 << 0;
+        final int flagOpaque = 1 << 9;
+        final int flags = (mDragIntent != null ? flagGlobal : 0) | flagOpaque;
+
+        mIsFrameworkDragActive = true;
+
+        if (!mDragView.startDrag(dragData, shadowBuilder, null, flags)) {
+            mIsFrameworkDragActive = false;
+            mEventListener.onDriverDragCancel();
+            return;
+        }
+
+        // Starting from this point, the driver takes over showing the drag shadow, so hiding the
+        // drag view.
+        mDragView.setVisibility(View.INVISIBLE);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        return !mIsFrameworkDragActive && super.onTouchEvent(ev);
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        return !mIsFrameworkDragActive && super.onInterceptTouchEvent(ev);
+    }
+
+    @Override
+    public boolean onDragEvent (DragEvent event) {
+        if (!mIsFrameworkDragActive) {
+            // We are interested only in drag events started by this driver.
+            return false;
+        }
+
+        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:
+                mLastX = event.getX();
+                mLastY = event.getY();
+                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 = true;
+                return true;
+
+            case DragEvent.ACTION_DRAG_EXITED:
+                mLastX = event.getX();
+                mLastY = event.getY();
+                mEventListener.onDriverDragExitWindow();
+                return true;
+
+            case DragEvent.ACTION_DRAG_ENDED:
+                final boolean dragAccepted = event.getResult();
+                final boolean acceptedByAnotherWindow = dragAccepted && !mReceivedDropEvent;
+
+                // When the system drag ends, its drag shadow disappears. Resume showing the drag
+                // view for the possible final animation.
+                mDragView.setVisibility(View.VISIBLE);
+
+                final DropTarget dropTargetOverride = acceptedByAnotherWindow ?
+                        new AnotherWindowDropTarget(mDragView.getContext()) : null;
+
+                mEventListener.onDriverDragEnd(mLastX, mLastY, dropTargetOverride);
+                mIsFrameworkDragActive = false;
+                return true;
+
+            default:
+                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 void onDragViewAnimationEnd() {}
+
+    @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 91%
rename from src/com/android/launcher3/DragLayer.java
rename to src/com/android/launcher3/dragndrop/DragLayer.java
index ad9063c..33ce683 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.dragndrop;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -26,8 +26,11 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Rect;
+import android.graphics.Region;
 import android.graphics.drawable.Drawable;
 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,7 +42,23 @@
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
+import com.android.launcher3.AppWidgetResizeFrame;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.InsettableFrameLayout;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetHostView;
+import com.android.launcher3.PinchToOverviewListener;
+import com.android.launcher3.R;
+import com.android.launcher3.SearchDropTargetBar;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.folder.Folder;
+import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -76,6 +95,7 @@
 
     private boolean mHoverPointClosesFolder = false;
     private final Rect mHitRect = new Rect();
+    private final Rect mHighlightRect = new Rect();
 
     private TouchCompleteListener mTouchCompleteListener;
 
@@ -95,8 +115,8 @@
     private Drawable mLeftHoverDrawableActive;
     private Drawable mRightHoverDrawableActive;
 
-    private boolean mBlockTouches = false;
-
+    // Related to pinch-to-go-to-overview gesture.
+    private PinchToOverviewListener mPinchListener = null;
     /**
      * Used to create a new DragLayer from XML.
      *
@@ -121,6 +141,10 @@
     public void setup(Launcher launcher, DragController controller) {
         mLauncher = launcher;
         mDragController = controller;
+
+        boolean isAccessibilityEnabled = ((AccessibilityManager) mLauncher.getSystemService(
+                Context.ACCESSIBILITY_SERVICE)).isEnabled();
+        onAccessibilityStateChanged(isAccessibilityEnabled);
     }
 
     @Override
@@ -128,6 +152,11 @@
         return mDragController.dispatchKeyEvent(event) || super.dispatchKeyEvent(event);
     }
 
+    public void onAccessibilityStateChanged(boolean isAccessibilityEnabled) {
+        mPinchListener = FeatureFlags.LAUNCHER3_DISABLE_PINCH_TO_OVERVIEW || isAccessibilityEnabled
+                ? null : new PinchToOverviewListener(mLauncher);
+    }
+
     public void showOverlayView(View overlayView) {
         LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
         mOverlayView = overlayView;
@@ -163,11 +192,12 @@
         if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
             return true;
         }
-        return false;
-    }
 
-    public void setBlockTouch(boolean block) {
-        mBlockTouches = block;
+        getDescendantRectRelativeToSelf(mLauncher.getAppInfoDropTargetBar(), mHitRect);
+        if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
+            return true;
+        }
+        return false;
     }
 
     private boolean handleTouchDown(MotionEvent ev, boolean intercept) {
@@ -175,10 +205,6 @@
         int x = (int) ev.getX();
         int y = (int) ev.getY();
 
-        if (mBlockTouches) {
-            return true;
-        }
-
         for (AppWidgetResizeFrame child: mResizeFrames) {
             child.getHitRect(hitRect);
             if (hitRect.contains(x, y)) {
@@ -231,6 +257,11 @@
             mTouchCompleteListener = null;
         }
         clearAllResizeFrames();
+
+        if (mPinchListener != null && mPinchListener.onInterceptTouchEvent(ev)) {
+            // Stop listening for scrolling etc. (onTouchEvent() handles the rest of the pinch.)
+            return true;
+        }
         return mDragController.onInterceptTouchEvent(ev);
     }
 
@@ -322,6 +353,7 @@
 
             if (isInAccessibleDrag()) {
                 childrenForAccessibility.add(mLauncher.getSearchDropTargetBar());
+                childrenForAccessibility.add(mLauncher.getAppInfoDropTargetBar());
             }
         } else {
             super.addChildrenForAccessibility(childrenForAccessibility);
@@ -343,8 +375,10 @@
         int x = (int) ev.getX();
         int y = (int) ev.getY();
 
-        if (mBlockTouches) {
-            return true;
+        // This is only reached if a pinch was started from onInterceptTouchEvent();
+        // this continues sending events for it.
+        if (mPinchListener != null) {
+            mPinchListener.onTouchEvent(ev);
         }
 
         if (action == MotionEvent.ACTION_DOWN) {
@@ -375,6 +409,11 @@
         return mDragController.onTouchEvent(ev);
     }
 
+    @Override
+    public boolean onDragEvent (DragEvent event) {
+        return mDragController.onDragEvent(event);
+    }
+
     /**
      * Determine the rect of the descendant in this DragLayer's coordinates
      *
@@ -760,7 +799,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) {
@@ -870,7 +909,7 @@
         }
     }
 
-    void onEnterScrollArea(int direction) {
+    void onEnterScrollArea() {
         mInScrollArea = true;
         invalidate();
     }
@@ -880,7 +919,7 @@
         invalidate();
     }
 
-    void showPageHints() {
+    public void showPageHints() {
         mShowPageHints = true;
         Workspace workspace = mLauncher.getWorkspace();
         getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.numCustomPages()),
@@ -888,17 +927,34 @@
         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();
         }
 
         super.dispatchDraw(canvas);
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 82%
rename from src/com/android/launcher3/DragView.java
rename to src/com/android/launcher3/dragndrop/DragView.java
index a584667..b1df41b 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,8 +36,14 @@
 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 {
@@ -45,19 +54,18 @@
     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;
@@ -70,7 +78,6 @@
      * <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.
@@ -81,10 +88,11 @@
             int left, int top, int width, int height, final float initialScale) {
         super(launcher);
         mDragLayer = launcher.getDragLayer();
-        mInitialScale = initialScale;
+        mDragController = launcher.getDragController();
 
         final Resources res = getResources();
-        final float scaleDps = res.getDimensionPixelSize(R.dimen.dragViewScale);
+        final float scaleDps = !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND ? 0f
+                : res.getDimensionPixelSize(R.dimen.dragViewScale);
         final float scale = (width + scaleDps) / width;
 
         // Set the initial scale to avoid any jumps
@@ -99,11 +107,6 @@
             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,9 +115,15 @@
 
                 if (getParent() == null) {
                     animation.cancel();
-                } else {
-                    setTranslationX(getTranslationX() + deltaX);
-                    setTranslationY(getTranslationY() + deltaY);
+                }
+            }
+        });
+
+        mAnim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!mAnimationCancelled) {
+                    mDragController.onDragViewAnimationEnd();
                 }
             }
         });
@@ -145,10 +154,6 @@
         return mIntrinsicIconScale;
     }
 
-    public float getOffsetY() {
-        return mOffsetY;
-    }
-
     public int getDragRegionLeft() {
         return mDragRegion.left;
     }
@@ -181,30 +186,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 +222,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 +243,7 @@
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 mCrossFadeProgress = animation.getAnimatedFraction();
+                invalidate();
             }
         });
         va.start();
@@ -328,16 +337,12 @@
     }
 
     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.
      *
@@ -345,8 +350,8 @@
      * @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);
+        setTranslationX(touchX - mRegistrationX);
+        setTranslationY(touchY - mRegistrationY);
     }
 
     void remove() {
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..89594f4
--- /dev/null
+++ b/src/com/android/launcher3/dynamicui/ColorExtractionService.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.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;
+
+/**
+ * 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 separately,
+            // since it only considers the lower part of the wallpaper.
+            // TODO(twickham): update Palette library to 23.3.1 or higher,
+            // which fixes a bug with using regions (b/28349435).
+            Palette hotseatPalette = Palette.from(wallpaper)
+                    .setRegion(0, (int) (wallpaper.getHeight() * (1f - HOTSEAT_FRACTION)),
+                            wallpaper.getWidth(), wallpaper.getHeight())
+                    .clearFilters()
+                    .generate();
+            extractedColors.updateHotseatPalette(hotseatPalette);
+        }
+
+        // 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..e545288
--- /dev/null
+++ b/src/com/android/launcher3/dynamicui/ExtractedColors.java
@@ -0,0 +1,159 @@
+/*
+ * 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 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 = 1;
+    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);
+    }
+}
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 89%
rename from src/com/android/launcher3/Folder.java
rename to src/com/android/launcher3/folder/Folder.java
index 471c324..1ebe8fd 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;
@@ -29,19 +29,19 @@
 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;
@@ -51,13 +51,35 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.launcher3.Alarm;
+import com.android.launcher3.CellLayout;
 import com.android.launcher3.CellLayout.CellInfo;
-import com.android.launcher3.DragController.DragListener;
+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.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.logging.UserEventDispatcher.LaunchSourceProvider;
+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.UiThreadCircularReveal;
 
@@ -70,8 +92,8 @@
  */
 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, AccessibilityDragSource,
+        LaunchSourceProvider {
     private static final String TAG = "Launcher.Folder";
 
     /**
@@ -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 View mFooter;
     private int mFooterHeight;
@@ -133,7 +155,15 @@
     // 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;
@@ -148,6 +178,7 @@
     @Thunk float mFolderIconPivotY;
     private boolean mIsEditingName = false;
 
+    @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mDestroyed;
 
     @Thunk Runnable mDeferredAction;
@@ -208,8 +239,26 @@
         });
         mFolderName.setOnFocusChangeListener(this);
 
-        // We disable action mode for now since it messes up the view on phones
-        mFolderName.setCustomSelectionActionModeCallback(mActionModeCallback);
+        if (!Utilities.ATLEAST_MARSHMALLOW) {
+            // We disable action mode in older OSes where floating selection menu is not yet
+            // available.
+            mFolderName.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
+                public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+                    return false;
+                }
+
+                public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+                    return false;
+                }
+
+                public void onDestroyActionMode(ActionMode mode) {
+                }
+
+                public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+                    return false;
+                }
+            });
+        }
         mFolderName.setOnEditorActionListener(this);
         mFolderName.setSelectAllOnFocus(true);
         mFolderName.setInputType(mFolderName.getInputType() |
@@ -224,23 +273,6 @@
         mFooterHeight = mFooter.getMeasuredHeight();
     }
 
-    private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
-        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-            return false;
-        }
-
-        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-            return false;
-        }
-
-        public void onDestroyActionMode(ActionMode mode) {
-        }
-
-        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-            return false;
-        }
-    };
-
     public void onClick(View v) {
         Object tag = v.getTag();
         if (tag instanceof ShortcutInfo) {
@@ -269,7 +301,7 @@
             mCurrentDragView = v;
 
             mContent.removeItem(mCurrentDragView);
-            mInfo.remove(mCurrentDragInfo);
+            mInfo.remove(mCurrentDragInfo, true);
             mDragInProgress = true;
             mItemAddedBackToSelfViaIcon = false;
         }
@@ -289,7 +321,7 @@
         }
 
         mFooter.setImportantForAccessibility(enable ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS :
-            IMPORTANT_FOR_ACCESSIBILITY_AUTO);
+                IMPORTANT_FOR_ACCESSIBILITY_AUTO);
         mLauncher.getWorkspace().setAddNewPageOnDrag(!enable);
     }
 
@@ -298,8 +330,13 @@
     }
 
     public void startEditingFolderName() {
-        mFolderName.setHint("");
-        mIsEditingName = true;
+        post(new Runnable() {
+            @Override
+            public void run() {
+                mFolderName.setHint("");
+                mIsEditingName = true;
+            }
+        });
     }
 
     public void dismissEditingName() {
@@ -317,7 +354,7 @@
 
         if (commit) {
             sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
-                    String.format(getContext().getString(R.string.folder_renamed), newTitle));
+                    getContext().getString(R.string.folder_renamed, newTitle));
         }
 
         // This ensures that focus is gained every time the field is clicked, which selects all
@@ -358,11 +395,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
      */
@@ -380,8 +432,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);
         }
 
@@ -423,8 +476,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);
     }
 
     /**
@@ -466,11 +519,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;
 
@@ -493,8 +542,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);
@@ -535,7 +584,7 @@
                 @Override
                 public void run() {
                     mContentWrapper.setLayerType(LAYER_TYPE_NONE, null);
-                    mContentWrapper.setLayerType(LAYER_TYPE_NONE, null);
+                    mFooter.setLayerType(LAYER_TYPE_NONE, null);
                 }
             };
         }
@@ -570,6 +619,7 @@
             final boolean updateAnimationFlag = !mDragInProgress;
             openFolderAnim.addListener(new AnimatorListenerAdapter() {
 
+                @SuppressLint("InlinedApi")
                 @Override
                 public void onAnimationEnd(Animator animation) {
                     mFolderName.animate().setDuration(FOLDER_NAME_ANIMATION_DURATION)
@@ -597,8 +647,7 @@
             mDragController.forceTouchMove();
         }
 
-        FolderPagedView pages = (FolderPagedView) mContent;
-        pages.verifyVisibleHighResIcons(pages.getNextPage());
+        mContent.verifyVisibleHighResIcons(mContent.getNextPage());
     }
 
     public void beginExternalDrag(ShortcutInfo item) {
@@ -613,7 +662,7 @@
     }
 
     @Override
-    public void onDragStart(DragSource source, Object info, int dragAction) { }
+    public void onDragStart(DragSource source, ItemInfo info, int dragAction) { }
 
     @Override
     public void onDragEnd() {
@@ -636,12 +685,7 @@
 
     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) {
@@ -689,7 +733,7 @@
     }
 
     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) &&
@@ -905,6 +949,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
@@ -913,7 +963,7 @@
     }
 
     @Override
-    public void onUninstallActivityReturned(boolean success) {
+    public void onDragObjectRemoved(boolean success) {
         mDeferDropAfterUninstall = false;
         mUninstallSuccessful = success;
         if (mDeferredAction != null) {
@@ -933,7 +983,7 @@
 
     @Override
     public boolean supportsAppInfoDropTarget() {
-        return false;
+        return !FeatureFlags.LAUNCHER3_LEGACY_WORKSPACE_DND;
     }
 
     @Override
@@ -964,16 +1014,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;
@@ -1010,8 +1050,13 @@
                 sTempRect.left + sTempRect.width() - 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)
+
+        int distFromEdgeOfScreen = grid.getWorkspacePadding(isLayoutRtl()).left + 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
@@ -1036,10 +1081,10 @@
         lp.y = top;
     }
 
-    float getPivotXForIconAnimation() {
+    public float getPivotXForIconAnimation() {
         return mFolderIconPivotX;
     }
-    float getPivotYForIconAnimation() {
+    public float getPivotYForIconAnimation() {
         return mFolderIconPivotY;
     }
 
@@ -1110,11 +1155,6 @@
         mItemsInvalidated = true;
     }
 
-    // TODO remove this once GSA code fix is submitted
-    public ViewGroup getContent() {
-        return (ViewGroup) mContent;
-    }
-
     public int getItemCount() {
         return mContent.getItemCount();
     }
@@ -1167,7 +1207,7 @@
         mDestroyed = true;
     }
 
-    boolean isDestroyed() {
+    public boolean isDestroyed() {
         return mDestroyed;
     }
 
@@ -1274,7 +1314,7 @@
 
         // Temporarily suppress the listener, as we did all the work already here.
         mSuppressOnAdd = true;
-        mInfo.add(si);
+        mInfo.add(si, false);
         mSuppressOnAdd = false;
         // Clear the drag info, as it is no longer being dragged.
         mCurrentDragInfo = null;
@@ -1334,13 +1374,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();
     }
 
@@ -1353,7 +1394,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;
                 }
@@ -1363,10 +1404,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) {
@@ -1385,11 +1422,12 @@
     }
 
     @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.itemType = LauncherLogProto.APP_ICON;
+        target.gridX = info.cellX;
+        target.gridY = info.cellY;
+        target.pageIndex = mContent.getCurrentPage();
+        targetParent.containerType = LauncherLogProto.FOLDER;
     }
 
     private class OnScrollHintListener implements OnAlarmListener {
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
new file mode 100644
index 0000000..1e4eb7f
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -0,0 +1,1012 @@
+/*
+ * 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();
+    @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(new SimpleOnStylusPressListener(this), this);
+        mPreviewLayoutRule = FeatureFlags.LAUNCHER3_LEGACY_FOLDER_ICON ?
+                new StackFolderIconLayoutRule() :
+                new ClippedFolderIconLayoutRule();
+
+        setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
+    }
+
+    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);
+
+        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) &&
+                !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);
+        }
+        mDragInfo = 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());
+
+        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, 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() {
+        mBackground.animateToRest();
+        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, 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, d);
+    }
+
+    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 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 95%
rename from src/com/android/launcher3/FolderPagedView.java
rename to src/com/android/launcher3/folder/FolderPagedView.java
index d503d2c..1af1485 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3;
+package com.android.launcher3.folder;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -23,13 +23,31 @@
 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.FocusIndicatorView;
+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.PageIndicator;
 import com.android.launcher3.PageIndicator.PageMarkerResources;
+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.util.Thunk;
 
 import java.util.ArrayList;
@@ -68,12 +86,17 @@
 
     @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;
@@ -226,12 +249,6 @@
         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());
     }
@@ -437,7 +454,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 +464,7 @@
     }
 
     public String getAccessibilityDescription() {
-        return String.format(getContext().getString(R.string.folder_opened),
-                mGridCountX, mGridCountY);
+        return getContext().getString(R.string.folder_opened, mGridCountX, mGridCountY);
     }
 
     /**
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/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
new file mode 100644
index 0000000..68d9b8c
--- /dev/null
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -0,0 +1,216 @@
+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) {
+        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..584e38e
--- /dev/null
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -0,0 +1,125 @@
+package com.android.launcher3.logging;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.ShortcutInfo;
+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";
+            default: return "UNKNOWN";
+        }
+    }
+
+    public static String getTargetStr(Target t) {
+        String 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 = "";
+        switch(t.itemType){
+            case LauncherLogProto.APP_ICON: typeStr = "ICON"; break;
+            case LauncherLogProto.SHORTCUT: typeStr = "SHORTCUT"; break;
+            case LauncherLogProto.WIDGET: typeStr = "WIDGET"; break;
+            default: typeStr = "UNKNOWN";
+        }
+
+        return typeStr + ", packageHash=" + t.packageNameHash
+                + ", componentHash=" + t.componentHash
+                + ", intentHash=" + t.intentHash
+                + ", grid=(" + t.gridX + "," + t.gridY + "), id=" + t.pageIndex;
+    }
+
+    private static String getControlStr(Target t) {
+        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;
+        Log.d(TAG, "t.containerType" + t.containerType);
+        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;
+            default:
+                str = "UNKNOWN";
+        }
+        return str + " id=" + t.pageIndex;
+    }
+
+
+    public static LauncherLogProto.LauncherEvent initLauncherEvent(
+            int actionType,
+            int childTargetType,
+            int parentTargetType){
+        LauncherLogProto.LauncherEvent event = new LauncherLogProto.LauncherEvent();
+
+        event.srcTarget = new LauncherLogProto.Target[2];
+        event.srcTarget[0] = new LauncherLogProto.Target();
+        event.srcTarget[0].type = childTargetType;
+        event.srcTarget[1] = new LauncherLogProto.Target();
+        event.srcTarget[1].type = parentTargetType;
+
+        event.action = new LauncherLogProto.Action();
+        event.action.type = actionType;
+        return event;
+    }
+}
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
new file mode 100644
index 0000000..e9a897e
--- /dev/null
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -0,0 +1,164 @@
+/*
+ * 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.view.View;
+import android.view.ViewParent;
+
+import com.android.launcher3.ItemInfo;
+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;
+
+/**
+ * Manages the creation of {@link LauncherEvent}.
+ */
+public abstract class UserEventDispatcher {
+
+    private final static int MAXIMUM_VIEW_HIERARCHY_LEVEL = 5;
+    /**
+     * 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;
+
+    //                      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, Target.ITEM, 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);
+        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));
+            }
+        }
+
+        // Fill in the duration of time spent navigating in Launcher and the container.
+        event.elapsedContainerMillis = System.currentTimeMillis() - mElapsedContainerMillis;
+        event.elapsedSessionMillis = System.currentTimeMillis() - mElapsedSessionMillis;
+        return event;
+    }
+
+    public void logAppLaunch(View v, Intent intent) {
+        dispatchUserEvent(createLauncherEvent(v, intent), intent);
+    }
+
+    public void logTap(View v) {
+        // TODO
+    }
+
+    public void logLongPress() {
+        // TODO
+    }
+
+    public void logDragNDrop() {
+        // TODO
+    }
+
+    public void setPredictedApps(List<ComponentKey> predictedApps) {
+        mPredictedApps = predictedApps;
+    }
+
+    /**
+     * Currently logs following containers: workspace, allapps, widget tray.
+     */
+    public final void resetElapsedContainerMillis() {
+        mElapsedContainerMillis = System.currentTimeMillis();
+    }
+
+    public final void resetElapsedSessionMillis() {
+        mElapsedSessionMillis = System.currentTimeMillis();
+        mElapsedContainerMillis = System.currentTimeMillis();
+
+    }
+
+    public final void resetActionDurationMillis() {
+        mActionDurationMillis = System.currentTimeMillis();
+    }
+
+    public abstract void dispatchUserEvent(LauncherEvent ev, Intent intent);
+
+    public int getPredictedRank(ComponentKey key) {
+        if (mPredictedApps == null) return -1;
+        return mPredictedApps.indexOf(key);
+    }
+}
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
new file mode 100644
index 0000000..e054734
--- /dev/null
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -0,0 +1,1034 @@
+package com.android.launcher3.model;
+
+import android.content.ComponentName;
+import android.content.ContentProviderOperation;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.database.Cursor;
+import android.graphics.Point;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherProvider;
+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.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.util.LongArrayMap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Locale;
+
+/**
+ * This class takes care of shrinking the workspace (by maximum of one row and one column), as a
+ * result of restoring from a larger device or device density change.
+ */
+public class GridSizeMigrationTask {
+
+    public static boolean ENABLED = Utilities.isNycOrAbove();
+
+    private static final String TAG = "GridSizeMigrationTask";
+    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";
+
+    // These are carefully selected weights for various item types (Math.random?), to allow for
+    // the least absurd migration experience.
+    private static final float WT_SHORTCUT = 1;
+    private static final float WT_APPLICATION = 0.8f;
+    private static final float WT_WIDGET_MIN = 2;
+    private static final float WT_WIDGET_FACTOR = 0.6f;
+    private static final float WT_FOLDER_FACTOR = 0.5f;
+
+    private final Context mContext;
+    private final InvariantDeviceProfile mIdp;
+
+    private final HashMap<String, Point> mWidgetMinSize = new HashMap<>();
+    private final ContentValues mTempValues = new ContentValues();
+    private final ArrayList<Long> mEntryToRemove = new ArrayList<>();
+    private final ArrayList<ContentProviderOperation> mUpdateOperations = new ArrayList<>();
+    private final ArrayList<DbEntry> mCarryOver = new ArrayList<>();
+    private final HashSet<String> mValidPackages;
+
+    private final int mSrcX, mSrcY;
+    private final int mTrgX, mTrgY;
+    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) {
+        mContext = context;
+        mValidPackages = validPackages;
+        mWidgetMinSize.putAll(widgetMinSize);
+        mIdp = idp;
+
+        mSrcX = sourceSize.x;
+        mSrcY = sourceSize.y;
+
+        mTrgX = targetSize.x;
+        mTrgY = targetSize.y;
+
+        mShouldRemoveX = mTrgX < mSrcX;
+        mShouldRemoveY = mTrgY < mSrcY;
+
+        // Non-used variables
+        mSrcHotseatSize = mSrcAllAppsRank = mDestHotseatSize = mDestAllAppsRank = -1;
+    }
+
+    protected GridSizeMigrationTask(Context context,
+            InvariantDeviceProfile idp, HashSet<String> validPackages,
+            int srcHotseatSize, int srcAllAppsRank,
+            int destHotseatSize, int destAllAppsRank) {
+        mContext = context;
+        mIdp = idp;
+        mValidPackages = validPackages;
+
+        mSrcHotseatSize = srcHotseatSize;
+        mSrcAllAppsRank = srcAllAppsRank;
+
+        mDestHotseatSize = destHotseatSize;
+        mDestAllAppsRank = destAllAppsRank;
+
+        // Non-used variables
+        mSrcX = mSrcY = mTrgX = mTrgY = -1;
+        mShouldRemoveX = mShouldRemoveY = false;
+    }
+
+    /**
+     * Applied all the pending DB operations
+     * @return true if any DB operation was commited.
+     */
+    private boolean applyOperations() throws Exception {
+        // Update items
+        if (!mUpdateOperations.isEmpty()) {
+            mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, mUpdateOperations);
+        }
+
+        if (!mEntryToRemove.isEmpty()) {
+            if (DEBUG) {
+                Log.d(TAG, "Removing items: " + TextUtils.join(", ", mEntryToRemove));
+            }
+            mContext.getContentResolver().delete(LauncherSettings.Favorites.CONTENT_URI,
+                    Utilities.createDbSelectionQuery(
+                            LauncherSettings.Favorites._ID, mEntryToRemove), null);
+        }
+
+        return !mUpdateOperations.isEmpty() || !mEntryToRemove.isEmpty();
+    }
+
+    /**
+     * To migrate hotseat, we load all the entries in order (LTR or RTL) and arrange them
+     * in the order in the new hotseat while keeping an empty space for all-apps. If the number of
+     * entries is more than what can fit in the new hotseat, we drop the entries with least weight.
+     * For weight calculation {@see #WT_SHORTCUT}, {@see #WT_APPLICATION}
+     * & {@see #WT_FOLDER_FACTOR}.
+     * @return true if any DB change was made
+     */
+    protected boolean migrateHotseat() throws Exception {
+        ArrayList<DbEntry> items = loadHotseatEntries();
+
+        int requiredCount = mDestHotseatSize - 1;
+
+        while (items.size() > requiredCount) {
+            // Pick the center item by default.
+            DbEntry toRemove = items.get(items.size() / 2);
+
+            // Find the item with least weight.
+            for (DbEntry entry : items) {
+                if (entry.weight < toRemove.weight) {
+                    toRemove = entry;
+                }
+            }
+
+            mEntryToRemove.add(toRemove.id);
+            items.remove(toRemove);
+        }
+
+        // Update screen IDS
+        int newScreenId = 0;
+        for (DbEntry entry : items) {
+            if (entry.screenId != newScreenId) {
+                entry.screenId = newScreenId;
+
+                // These values does not affect the item position, but we should set them
+                // to something other than -1.
+                entry.cellX = newScreenId;
+                entry.cellY = 0;
+
+                update(entry);
+            }
+
+            newScreenId++;
+            if (newScreenId == mDestAllAppsRank) {
+                newScreenId++;
+            }
+        }
+
+        return applyOperations();
+    }
+
+    /**
+     * @return true if any DB change was made
+     */
+    protected boolean migrateWorkspace() throws Exception {
+        ArrayList<Long> allScreens = LauncherModel.loadWorkspaceScreensDb(mContext);
+        if (allScreens.isEmpty()) {
+            throw new Exception("Unable to get workspace screens");
+        }
+
+        for (long screenId : allScreens) {
+            if (DEBUG) {
+                Log.d(TAG, "Migrating " + screenId);
+            }
+            migrateScreen(screenId);
+        }
+
+        if (!mCarryOver.isEmpty()) {
+            LongArrayMap<DbEntry> itemMap = new LongArrayMap<>();
+            for (DbEntry e : mCarryOver) {
+                itemMap.put(e.id, e);
+            }
+
+            do {
+                // Some items are still remaining. Try adding a few new screens.
+
+                // At every iteration, make sure that at least one item is removed from
+                // {@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);
+                placement.find();
+                if (placement.finalPlacedItems.size() > 0) {
+                    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))) {
+                            throw new Exception("Unable to find matching items");
+                        }
+                        item.screenId = newScreenId;
+                        update(item);
+                    }
+                } else {
+                    throw new Exception("None of the items can be placed on an empty screen");
+                }
+
+            } while (!mCarryOver.isEmpty());
+
+            // Update screens
+            final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
+            mUpdateOperations.add(ContentProviderOperation.newDelete(uri).build());
+            int count = allScreens.size();
+            for (int i = 0; i < count; i++) {
+                ContentValues v = new ContentValues();
+                long screenId = allScreens.get(i);
+                v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
+                v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
+                mUpdateOperations.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
+            }
+        }
+        return applyOperations();
+    }
+
+    /**
+     * 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[])}
+     *   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) {
+        ArrayList<DbEntry> items = loadWorkspaceEntries(screenId);
+
+        int removedCol = Integer.MAX_VALUE;
+        int removedRow = Integer.MAX_VALUE;
+
+        // removeWt represents the cost function for loss of items during migration, and moveWt
+        // represents the cost function for repositioning the items. moveWt is only considered if
+        // removeWt is same for two different configurations.
+        // Start with Float.MAX_VALUE (assuming full data) and pick the configuration with least
+        // cost.
+        float removeWt = Float.MAX_VALUE;
+        float moveWt = Float.MAX_VALUE;
+        float[] outLoss = new float[2];
+        ArrayList<DbEntry> finalItems = null;
+
+        // Try removing all possible combinations
+        for (int x = 0; x < mSrcX; x++) {
+            for (int y = 0; y < mSrcY; 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);
+
+                if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1] < moveWt))) {
+                    removeWt = outLoss[0];
+                    moveWt = outLoss[1];
+                    removedCol = mShouldRemoveX ? x : removedCol;
+                    removedRow = mShouldRemoveY ? y : removedRow;
+                    finalItems = itemsOnScreen;
+                }
+
+                // No need to loop over all rows, if a row removal is not needed.
+                if (!mShouldRemoveY) {
+                    break;
+                }
+            }
+
+            if (!mShouldRemoveX) {
+                break;
+            }
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, String.format("Removing row %d, column %d on screen %d",
+                    removedRow, removedCol, screenId));
+        }
+
+        LongArrayMap<DbEntry> itemMap = new LongArrayMap<>();
+        for (DbEntry e : deepCopy(items)) {
+            itemMap.put(e.id, e);
+        }
+
+        for (DbEntry item : finalItems) {
+            DbEntry org = itemMap.get(item.id);
+            itemMap.remove(item.id);
+
+            // Check if update is required
+            if (!item.columnsSame(org)) {
+                update(item);
+            }
+        }
+
+        // The remaining items in {@link #itemMap} are those which didn't get placed.
+        for (DbEntry item : itemMap) {
+            mCarryOver.add(item);
+        }
+
+        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];
+            for (DbEntry item : finalItems) {
+                markCells(occupied, item, true);
+            }
+
+            OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied,
+                    deepCopy(mCarryOver), true);
+            placement.find();
+            if (placement.lowestWeightLoss == 0) {
+                // All items got placed
+
+                for (DbEntry item : placement.finalPlacedItems) {
+                    item.screenId = screenId;
+                    update(item);
+                }
+
+                mCarryOver.clear();
+            }
+        }
+    }
+
+    /**
+     * Updates an item in the DB.
+     */
+    private void update(DbEntry item) {
+        mTempValues.clear();
+        item.addToContentValues(mTempValues);
+        mUpdateOperations.add(ContentProviderOperation
+                .newUpdate(LauncherSettings.Favorites.getContentUri(item.id))
+                .withValues(mTempValues).build());
+    }
+
+    /**
+     * Tries the remove the provided row and column.
+     * @param items all the items on the screen under operation
+     * @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];
+
+        col = mShouldRemoveX ? col : Integer.MAX_VALUE;
+        row = mShouldRemoveY ? row : Integer.MAX_VALUE;
+
+        ArrayList<DbEntry> finalItems = new ArrayList<>();
+        ArrayList<DbEntry> removedItems = new ArrayList<>();
+
+        for (DbEntry item : items) {
+            if ((item.cellX <= col && (item.spanX + item.cellX) > col)
+                || (item.cellY <= row && (item.spanY + item.cellY) > row)) {
+                removedItems.add(item);
+                if (item.cellX >= col) item.cellX --;
+                if (item.cellY >= row) item.cellY --;
+            } else {
+                if (item.cellX > col) item.cellX --;
+                if (item.cellY > row) item.cellY --;
+                finalItems.add(item);
+                markCells(occupied, item, true);
+            }
+        }
+
+        OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied, removedItems);
+        placement.find();
+        finalItems.addAll(placement.finalPlacedItems);
+        outLoss[0] = placement.lowestWeightLoss;
+        outLoss[1] = placement.lowestMoveCost;
+        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;
+
+        // If set to true, item movement are not considered in move cost, leading to a more
+        // linear placement.
+        private final boolean ignoreMove;
+
+        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(boolean[][] occupied, ArrayList<DbEntry> itemsToPlace,
+                boolean ignoreMove) {
+            this.occupied = occupied;
+            this.itemsToPlace = itemsToPlace;
+            this.ignoreMove = ignoreMove;
+
+            // Sort the items such that larger widgets appear first followed by 1x1 items
+            Collections.sort(this.itemsToPlace);
+        }
+
+        public void find() {
+            find(0, 0, 0, new ArrayList<DbEntry>());
+        }
+
+        /**
+         * Recursively finds a placement for the provided items.
+         * @param index the position in {@link #itemsToPlace} to start looking at.
+         * @param weightLoss total weight loss upto this point
+         * @param moveCost total move cost upto this point
+         * @param itemsPlaced all the items already placed upto this point
+         */
+        public void find(int index, float weightLoss, float moveCost,
+                ArrayList<DbEntry> itemsPlaced) {
+            if ((weightLoss >= lowestWeightLoss) ||
+                    ((weightLoss == lowestWeightLoss) && (moveCost >= lowestMoveCost))) {
+                // Abort, as we already have a better solution.
+                return;
+
+            } else if (index >= itemsToPlace.size()) {
+                // End loop.
+                lowestWeightLoss = weightLoss;
+                lowestMoveCost = moveCost;
+
+                // Keep a deep copy of current configuration as it can change during recursion.
+                finalPlacedItems = deepCopy(itemsPlaced);
+                return;
+            }
+
+            DbEntry me = itemsToPlace.get(index);
+            int myX = me.cellX;
+            int myY = me.cellY;
+
+            // List of items to pass over if this item was placed.
+            ArrayList<DbEntry> itemsIncludingMe = new ArrayList<>(itemsPlaced.size() + 1);
+            itemsIncludingMe.addAll(itemsPlaced);
+            itemsIncludingMe.add(me);
+
+            if (me.spanX > 1 || me.spanY > 1) {
+                // If the current item is a widget (and it greater than 1x1), try to place it at
+                // all possible positions. This is because a widget placed at one position can
+                // affect the placement of a different widget.
+                int myW = me.spanX;
+                int myH = me.spanY;
+
+                for (int y = 0; y < mTrgY; y++) {
+                    for (int x = 0; x < mTrgX; x++) {
+                        float newMoveCost = moveCost;
+                        if (x != myX) {
+                            me.cellX = x;
+                            newMoveCost ++;
+                        }
+                        if (y != myY) {
+                            me.cellY = y;
+                            newMoveCost ++;
+                        }
+                        if (ignoreMove) {
+                            newMoveCost = moveCost;
+                        }
+
+                        if (isVacant(occupied, x, y, myW, myH)) {
+                            // place at this position and continue search.
+                            markCells(occupied, me, true);
+                            find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
+                            markCells(occupied, me, false);
+                        }
+
+                        // Try resizing horizontally
+                        if (myW > me.minSpanX && isVacant(occupied, x, y, myW - 1, myH)) {
+                            me.spanX --;
+                            markCells(occupied, me, true);
+                            // 1 extra move cost
+                            find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
+                            markCells(occupied, me, false);
+                            me.spanX ++;
+                        }
+
+                        // Try resizing vertically
+                        if (myH > me.minSpanY && isVacant(occupied, x, y, myW, myH - 1)) {
+                            me.spanY --;
+                            markCells(occupied, me, true);
+                            // 1 extra move cost
+                            find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
+                            markCells(occupied, me, false);
+                            me.spanY ++;
+                        }
+
+                        // Try resizing horizontally & vertically
+                        if (myH > me.minSpanY && myW > me.minSpanX &&
+                                isVacant(occupied, x, y, myW - 1, myH - 1)) {
+                            me.spanX --;
+                            me.spanY --;
+                            markCells(occupied, me, true);
+                            // 2 extra move cost
+                            find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe);
+                            markCells(occupied, me, false);
+                            me.spanX ++;
+                            me.spanY ++;
+                        }
+                        me.cellX = myX;
+                        me.cellY = myY;
+                    }
+                }
+
+                // Finally also try a solution when this item is not included. Trying it in the end
+                // causes it to get skipped in most cases due to higher weight loss, and prevents
+                // unnecessary deep copies of various configurations.
+                find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced);
+            } else {
+                // Since this is a 1x1 item and all the following items are also 1x1, just place
+                // it at 'the most appropriate position' and hope for the best.
+                // The most appropriate position: one with lease straight line distance
+                int newDistance = Integer.MAX_VALUE;
+                int newX = Integer.MAX_VALUE, newY = Integer.MAX_VALUE;
+
+                for (int y = 0; y < mTrgY; y++) {
+                    for (int x = 0; x < mTrgX; x++) {
+                        if (!occupied[x][y]) {
+                            int dist = ignoreMove ? 0 :
+                                ((me.cellX - x) * (me.cellX - x) + (me.cellY - y) * (me.cellY - y));
+                            if (dist < newDistance) {
+                                newX = x;
+                                newY = y;
+                                newDistance = dist;
+                            }
+                        }
+                    }
+                }
+
+                if (newX < mTrgX && newY < mTrgY) {
+                    float newMoveCost = moveCost;
+                    if (newX != myX) {
+                        me.cellX = newX;
+                        newMoveCost ++;
+                    }
+                    if (newY != myY) {
+                        me.cellY = newY;
+                        newMoveCost ++;
+                    }
+                    if (ignoreMove) {
+                        newMoveCost = moveCost;
+                    }
+                    markCells(occupied, me, true);
+                    find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
+                    markCells(occupied, me, false);
+                    me.cellX = myX;
+                    me.cellY = myY;
+
+                    // Try to find a solution without this item, only if
+                    //  1) there was at least one space, i.e., we were able to place this item
+                    //  2) if the next item has the same weight (all items are already sorted), as
+                    //     if it has lower weight, that solution will automatically get discarded.
+                    //  3) ignoreMove false otherwise, move cost is ignored and the weight will
+                    //      anyway be same.
+                    if (index + 1 < itemsToPlace.size()
+                            && itemsToPlace.get(index + 1).weight >= me.weight && !ignoreMove) {
+                        find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced);
+                    }
+                } else {
+                    // No more space. Jump to the end.
+                    for (int i = index + 1; i < itemsToPlace.size(); i++) {
+                        weightLoss += itemsToPlace.get(i).weight;
+                    }
+                    find(itemsToPlace.size(), weightLoss + me.weight, moveCost, itemsPlaced);
+                }
+            }
+        }
+    }
+
+    private ArrayList<DbEntry> loadHotseatEntries() {
+        Cursor c =  mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                new String[]{
+                        Favorites._ID,                  // 0
+                        Favorites.ITEM_TYPE,            // 1
+                        Favorites.INTENT,               // 2
+                        Favorites.SCREEN},              // 3
+                Favorites.CONTAINER + " = " + Favorites.CONTAINER_HOTSEAT, null, null, null);
+
+        final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
+        final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
+        final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT);
+        final int indexScreen = c.getColumnIndexOrThrow(Favorites.SCREEN);
+
+        ArrayList<DbEntry> entries = new ArrayList<>();
+        while (c.moveToNext()) {
+            DbEntry entry = new DbEntry();
+            entry.id = c.getLong(indexId);
+            entry.itemType = c.getInt(indexItemType);
+            entry.screenId = c.getLong(indexScreen);
+
+            if (entry.screenId >= mSrcHotseatSize) {
+                mEntryToRemove.add(entry.id);
+                continue;
+            }
+
+            try {
+                // calculate weight
+                switch (entry.itemType) {
+                    case Favorites.ITEM_TYPE_SHORTCUT:
+                    case Favorites.ITEM_TYPE_APPLICATION: {
+                        verifyIntent(c.getString(indexIntent));
+                        entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT
+                                ? WT_SHORTCUT : WT_APPLICATION;
+                        break;
+                    }
+                    case Favorites.ITEM_TYPE_FOLDER: {
+                        int total = getFolderItemsCount(entry.id);
+                        if (total == 0) {
+                            throw new Exception("Folder is empty");
+                        }
+                        entry.weight = WT_FOLDER_FACTOR * total;
+                        break;
+                    }
+                    default:
+                        throw new Exception("Invalid item type");
+                }
+            } catch (Exception e) {
+                if (DEBUG) {
+                    Log.d(TAG, "Removing item " + entry.id, e);
+                }
+                mEntryToRemove.add(entry.id);
+                continue;
+            }
+            entries.add(entry);
+        }
+        c.close();
+        return entries;
+    }
+
+
+    /**
+     * Loads entries for a particular screen id.
+     */
+    private ArrayList<DbEntry> loadWorkspaceEntries(long screen) {
+        Cursor c =  mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                new String[]{
+                        Favorites._ID,                  // 0
+                        Favorites.ITEM_TYPE,            // 1
+                        Favorites.CELLX,                // 2
+                        Favorites.CELLY,                // 3
+                        Favorites.SPANX,                // 4
+                        Favorites.SPANY,                // 5
+                        Favorites.INTENT,               // 6
+                        Favorites.APPWIDGET_PROVIDER,   // 7
+                        Favorites.APPWIDGET_ID},        // 8
+                Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP
+                        + " AND " + Favorites.SCREEN + " = " + screen, null, null, null);
+
+        final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
+        final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
+        final int indexCellX = c.getColumnIndexOrThrow(Favorites.CELLX);
+        final int indexCellY = c.getColumnIndexOrThrow(Favorites.CELLY);
+        final int indexSpanX = c.getColumnIndexOrThrow(Favorites.SPANX);
+        final int indexSpanY = c.getColumnIndexOrThrow(Favorites.SPANY);
+        final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT);
+        final int indexAppWidgetProvider = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER);
+        final int indexAppWidgetId = c.getColumnIndexOrThrow(Favorites.APPWIDGET_ID);
+
+        ArrayList<DbEntry> entries = new ArrayList<>();
+        while (c.moveToNext()) {
+            DbEntry entry = new DbEntry();
+            entry.id = c.getLong(indexId);
+            entry.itemType = c.getInt(indexItemType);
+            entry.cellX = c.getInt(indexCellX);
+            entry.cellY = c.getInt(indexCellY);
+            entry.spanX = c.getInt(indexSpanX);
+            entry.spanY = c.getInt(indexSpanY);
+            entry.screenId = screen;
+
+            try {
+                // calculate weight
+                switch (entry.itemType) {
+                    case Favorites.ITEM_TYPE_SHORTCUT:
+                    case Favorites.ITEM_TYPE_APPLICATION: {
+                        verifyIntent(c.getString(indexIntent));
+                        entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT
+                            ? WT_SHORTCUT : WT_APPLICATION;
+                        break;
+                    }
+                    case Favorites.ITEM_TYPE_APPWIDGET: {
+                        String provider = c.getString(indexAppWidgetProvider);
+                        ComponentName cn = ComponentName.unflattenFromString(provider);
+                        verifyPackage(cn.getPackageName());
+                        entry.weight = Math.max(WT_WIDGET_MIN, WT_WIDGET_FACTOR
+                                * entry.spanX * entry.spanY);
+
+                        int widgetId = c.getInt(indexAppWidgetId);
+                        LauncherAppWidgetProviderInfo pInfo = AppWidgetManagerCompat.getInstance(
+                                mContext).getLauncherAppWidgetInfo(widgetId);
+                        Point spans = pInfo == null ?
+                                mWidgetMinSize.get(provider) : pInfo.getMinSpans(mIdp, mContext);
+                        if (spans != null) {
+                            entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX;
+                            entry.minSpanY = spans.y > 0 ? spans.y : entry.spanY;
+                        } else {
+                            // Assume that the widget be resized down to 2x2
+                            entry.minSpanX = entry.minSpanY = 2;
+                        }
+
+                        if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) {
+                            throw new Exception("Widget can't be resized down to fit the grid");
+                        }
+                        break;
+                    }
+                    case Favorites.ITEM_TYPE_FOLDER: {
+                        int total = getFolderItemsCount(entry.id);
+                        if (total == 0) {
+                            throw new Exception("Folder is empty");
+                        }
+                        entry.weight = WT_FOLDER_FACTOR * total;
+                        break;
+                    }
+                    default:
+                        throw new Exception("Invalid item type");
+                }
+            } catch (Exception e) {
+                if (DEBUG) {
+                    Log.d(TAG, "Removing item " + entry.id, e);
+                }
+                mEntryToRemove.add(entry.id);
+                continue;
+            }
+            entries.add(entry);
+        }
+        c.close();
+        return entries;
+    }
+
+    /**
+     * @return the number of valid items in the folder.
+     */
+    private int getFolderItemsCount(long folderId) {
+        Cursor c =  mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                new String[]{Favorites._ID, Favorites.INTENT},
+                Favorites.CONTAINER + " = " + folderId, null, null, null);
+
+        int total = 0;
+        while (c.moveToNext()) {
+            try {
+                verifyIntent(c.getString(1));
+                total++;
+            } catch (Exception e) {
+                mEntryToRemove.add(c.getLong(0));
+            }
+        }
+        c.close();
+        return total;
+    }
+
+    /**
+     * Verifies if the intent should be restored.
+     */
+    private void verifyIntent(String intentStr) throws Exception {
+        Intent intent = Intent.parseUri(intentStr, 0);
+        if (intent.getComponent() != null) {
+            verifyPackage(intent.getComponent().getPackageName());
+        } else if (intent.getPackage() != null) {
+            // Only verify package if the component was null.
+            verifyPackage(intent.getPackage());
+        }
+    }
+
+    /**
+     * Verifies if the package should be restored
+     */
+    private void verifyPackage(String packageName) throws Exception {
+        if (!mValidPackages.contains(packageName)) {
+            throw new Exception("Package not available");
+        }
+    }
+
+    private static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
+
+        public float weight;
+
+        public DbEntry() { }
+
+        public DbEntry copy() {
+            DbEntry entry = new DbEntry();
+            entry.copyFrom(this);
+            entry.weight = weight;
+            entry.minSpanX = minSpanX;
+            entry.minSpanY = minSpanY;
+            return entry;
+        }
+
+        /**
+         * Comparator such that larger widgets come first,  followed by all 1x1 items
+         * based on their weights.
+         */
+        @Override
+        public int compareTo(DbEntry another) {
+            if (itemType == Favorites.ITEM_TYPE_APPWIDGET) {
+                if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
+                    return another.spanY * another.spanX - spanX * spanY;
+                } else {
+                    return -1;
+                }
+            } else if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
+                return 1;
+            } else {
+                // Place higher weight before lower weight.
+                return Float.compare(another.weight, weight);
+            }
+        }
+
+        public boolean columnsSame(DbEntry org) {
+            return org.cellX == cellX && org.cellY == cellY && org.spanX == spanX &&
+                    org.spanY == spanY && org.screenId == screenId;
+        }
+
+        public void addToContentValues(ContentValues values) {
+            values.put(LauncherSettings.Favorites.SCREEN, screenId);
+            values.put(LauncherSettings.Favorites.CELLX, cellX);
+            values.put(LauncherSettings.Favorites.CELLY, cellY);
+            values.put(LauncherSettings.Favorites.SPANX, spanX);
+            values.put(LauncherSettings.Favorites.SPANY, spanY);
+        }
+    }
+
+    private static ArrayList<DbEntry> deepCopy(ArrayList<DbEntry> src) {
+        ArrayList<DbEntry> dup = new ArrayList<DbEntry>(src.size());
+        for (DbEntry e : src) {
+            dup.add(e.copy());
+        }
+        return dup;
+    }
+
+    private static Point parsePoint(String point) {
+        String[] split = point.split(",");
+        return new Point(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
+    }
+
+    private static String getPointString(int x, int y) {
+        return String.format(Locale.ENGLISH, "%d,%d", x, y);
+    }
+
+    public static void markForMigration(
+            Context context, HashSet<String> widgets, BackupProtos.DeviceProfieData srcProfile) {
+        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)
+                .apply();
+    }
+
+    /**
+     * Migrates the workspace and hotseat in case their sizes changed.
+     * @return false if the migration failed.
+     */
+    public static boolean migrateGridIfNeeded(Context context) {
+        SharedPreferences prefs = Utilities.getPrefs(context);
+        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, ""))) {
+            // Skip if workspace and hotseat sizes have not changed.
+            return true;
+        }
+
+        long migrationStartTime = System.currentTimeMillis();
+        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());
+
+            // Hotseat
+            Point srcHotseatSize = parsePoint(prefs.getString(
+                    KEY_MIGRATION_SRC_HOTSEAT_SIZE, hotseatSizeString));
+            if (srcHotseatSize.x != idp.numHotseatIcons ||
+                    srcHotseatSize.y != idp.hotseatAllAppsRank) {
+                // Migrate hotseat.
+
+                dbChanged = new GridSizeMigrationTask(context,
+                        LauncherAppState.getInstance().getInvariantDeviceProfile(),
+                        validPackages,
+                        srcHotseatSize.x, srcHotseatSize.y,
+                        idp.numHotseatIcons, idp.hotseatAllAppsRank).migrateHotseat();
+            }
+
+            // Grid size
+            Point targetSize = new Point(idp.numColumns, idp.numRows);
+            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 (dbChanged) {
+                // Make sure we haven't removed everything.
+                final Cursor c = context.getContentResolver().query(
+                        LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
+                boolean hasData = c.moveToNext();
+                c.close();
+                if (!hasData) {
+                    throw new Exception("Removed every thing during grid resize");
+                }
+            }
+
+            return true;
+        } catch (Exception e) {
+            Log.e(TAG, "Error during grid migration", e);
+
+            return false;
+        } finally {
+            Log.v(TAG, "Workspace migration completed in "
+                    + (System.currentTimeMillis() - migrationStartTime));
+
+            // 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)
+                    .apply();
+        }
+    }
+}
diff --git a/src/com/android/launcher3/model/MigrateFromRestoreTask.java b/src/com/android/launcher3/model/MigrateFromRestoreTask.java
deleted file mode 100644
index 786ab60..0000000
--- a/src/com/android/launcher3/model/MigrateFromRestoreTask.java
+++ /dev/null
@@ -1,763 +0,0 @@
-package com.android.launcher3.model;
-
-import android.content.ComponentName;
-import android.content.ContentProviderOperation;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.database.Cursor;
-import android.graphics.Point;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.PackageInstallerCompat;
-import com.android.launcher3.compat.UserHandleCompat;
-import com.android.launcher3.util.LongArrayMap;
-import com.android.launcher3.util.Thunk;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-
-/**
- * This class takes care of shrinking the workspace (by maximum of one row and one column), as a
- * result of restoring from a larger device.
- */
-public class MigrateFromRestoreTask {
-
-    public static boolean ENABLED = false;
-
-    private static final String TAG = "MigrateFromRestoreTask";
-    private static final boolean DEBUG = true;
-
-    private static final String KEY_MIGRATION_SOURCE_SIZE = "migration_restore_src_size";
-    private static final String KEY_MIGRATION_WIDGET_MINSIZE = "migration_widget_min_size";
-
-    // These are carefully selected weights for various item types (Math.random?), to allow for
-    // the lease absurd migration experience.
-    private static final float WT_SHORTCUT = 1;
-    private static final float WT_APPLICATION = 0.8f;
-    private static final float WT_WIDGET_MIN = 2;
-    private static final float WT_WIDGET_FACTOR = 0.6f;
-    private static final float WT_FOLDER_FACTOR = 0.5f;
-
-    private final Context mContext;
-    private final ContentValues mTempValues = new ContentValues();
-    private final HashMap<String, Point> mWidgetMinSize;
-    private final InvariantDeviceProfile mIdp;
-
-    private HashSet<String> mValidPackages;
-    public ArrayList<Long> mEntryToRemove;
-    private ArrayList<ContentProviderOperation> mUpdateOperations;
-
-    private ArrayList<DbEntry> mCarryOver;
-
-    private final int mSrcX, mSrcY;
-    @Thunk final int mTrgX, mTrgY;
-    private final boolean mShouldRemoveX, mShouldRemoveY;
-
-    public MigrateFromRestoreTask(Context context) {
-        mContext = context;
-
-        SharedPreferences prefs = Utilities.getPrefs(context);
-        Point sourceSize = parsePoint(prefs.getString(KEY_MIGRATION_SOURCE_SIZE, ""));
-        mSrcX = sourceSize.x;
-        mSrcY = sourceSize.y;
-
-        mWidgetMinSize = new HashMap<String, Point>();
-        for (String s : prefs.getStringSet(KEY_MIGRATION_WIDGET_MINSIZE,
-                Collections.<String>emptySet())) {
-            String[] parts = s.split("#");
-            mWidgetMinSize.put(parts[0], parsePoint(parts[1]));
-        }
-
-        mIdp = LauncherAppState.getInstance().getInvariantDeviceProfile();
-        mTrgX = mIdp.numColumns;
-        mTrgY = mIdp.numRows;
-        mShouldRemoveX = mTrgX < mSrcX;
-        mShouldRemoveY = mTrgY < mSrcY;
-    }
-
-    public void execute() throws Exception {
-        mEntryToRemove = new ArrayList<>();
-        mCarryOver = new ArrayList<>();
-        mUpdateOperations = new ArrayList<>();
-
-        // 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.
-        mValidPackages = new HashSet<>();
-        for (PackageInfo info : mContext.getPackageManager().getInstalledPackages(0)) {
-            mValidPackages.add(info.packageName);
-        }
-        mValidPackages.addAll(PackageInstallerCompat.getInstance(mContext)
-                .updateAndGetActiveSessionCache().keySet());
-
-        ArrayList<Long> allScreens = LauncherModel.loadWorkspaceScreensDb(mContext);
-        if (allScreens.isEmpty()) {
-            throw new Exception("Unable to get workspace screens");
-        }
-
-        for (long screenId : allScreens) {
-            if (DEBUG) {
-                Log.d(TAG, "Migrating " + screenId);
-            }
-            migrateScreen(screenId);
-        }
-
-        if (!mCarryOver.isEmpty()) {
-            LongArrayMap<DbEntry> itemMap = new LongArrayMap<>();
-            for (DbEntry e : mCarryOver) {
-                itemMap.put(e.id, e);
-            }
-
-            do {
-                // Some items are still remaining. Try adding a few new screens.
-
-                // At every iteration, make sure that at least one item is removed from
-                // {@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);
-                placement.find();
-                if (placement.finalPlacedItems.size() > 0) {
-                    long newScreenId = LauncherAppState.getLauncherProvider().generateNewScreenId();
-                    allScreens.add(newScreenId);
-                    for (DbEntry item : placement.finalPlacedItems) {
-                        if (!mCarryOver.remove(itemMap.get(item.id))) {
-                            throw new Exception("Unable to find matching items");
-                        }
-                        item.screenId = newScreenId;
-                        update(item);
-                    }
-                } else {
-                    throw new Exception("None of the items can be placed on an empty screen");
-                }
-
-            } while (!mCarryOver.isEmpty());
-
-
-            LauncherAppState.getInstance().getModel()
-                .updateWorkspaceScreenOrder(mContext, allScreens);
-        }
-
-        // Update items
-        mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, mUpdateOperations);
-
-        if (!mEntryToRemove.isEmpty()) {
-            if (DEBUG) {
-                Log.d(TAG, "Removing items: " + TextUtils.join(", ", mEntryToRemove));
-            }
-            mContext.getContentResolver().delete(LauncherSettings.Favorites.CONTENT_URI,
-                    Utilities.createDbSelectionQuery(
-                            LauncherSettings.Favorites._ID, mEntryToRemove), null);
-        }
-
-        // Make sure we haven't removed everything.
-        final Cursor c = mContext.getContentResolver().query(
-                LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
-        boolean hasData = c.moveToNext();
-        c.close();
-        if (!hasData) {
-            throw new Exception("Removed every thing during grid resize");
-        }
-    }
-
-    /**
-     * 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[])}
-     *   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) {
-        ArrayList<DbEntry> items = loadEntries(screenId);
-
-        int removedCol = Integer.MAX_VALUE;
-        int removedRow = Integer.MAX_VALUE;
-
-        // removeWt represents the cost function for loss of items during migration, and moveWt
-        // represents the cost function for repositioning the items. moveWt is only considered if
-        // removeWt is same for two different configurations.
-        // Start with Float.MAX_VALUE (assuming full data) and pick the configuration with least
-        // cost.
-        float removeWt = Float.MAX_VALUE;
-        float moveWt = Float.MAX_VALUE;
-        float[] outLoss = new float[2];
-        ArrayList<DbEntry> finalItems = null;
-
-        // Try removing all possible combinations
-        for (int x = 0; x < mSrcX; x++) {
-            for (int y = 0; y < mSrcY; 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);
-
-                if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1] < moveWt))) {
-                    removeWt = outLoss[0];
-                    moveWt = outLoss[1];
-                    removedCol = mShouldRemoveX ? x : removedCol;
-                    removedRow = mShouldRemoveY ? y : removedRow;
-                    finalItems = itemsOnScreen;
-                }
-
-                // No need to loop over all rows, if a row removal is not needed.
-                if (!mShouldRemoveY) {
-                    break;
-                }
-            }
-
-            if (!mShouldRemoveX) {
-                break;
-            }
-        }
-
-        if (DEBUG) {
-            Log.d(TAG, String.format("Removing row %d, column %d on screen %d",
-                    removedRow, removedCol, screenId));
-        }
-
-        LongArrayMap<DbEntry> itemMap = new LongArrayMap<>();
-        for (DbEntry e : deepCopy(items)) {
-            itemMap.put(e.id, e);
-        }
-
-        for (DbEntry item : finalItems) {
-            DbEntry org = itemMap.get(item.id);
-            itemMap.remove(item.id);
-
-            // Check if update is required
-            if (!item.columnsSame(org)) {
-                update(item);
-            }
-        }
-
-        // The remaining items in {@link #itemMap} are those which didn't get placed.
-        for (DbEntry item : itemMap) {
-            mCarryOver.add(item);
-        }
-
-        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];
-            for (DbEntry item : finalItems) {
-                markCells(occupied, item, true);
-            }
-
-            OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied,
-                    deepCopy(mCarryOver), true);
-            placement.find();
-            if (placement.lowestWeightLoss == 0) {
-                // All items got placed
-
-                for (DbEntry item : placement.finalPlacedItems) {
-                    item.screenId = screenId;
-                    update(item);
-                }
-
-                mCarryOver.clear();
-            }
-        }
-    }
-
-    /**
-     * Updates an item in the DB.
-     */
-    private void update(DbEntry item) {
-        mTempValues.clear();
-        item.addToContentValues(mTempValues);
-        mUpdateOperations.add(ContentProviderOperation
-                .newUpdate(LauncherSettings.Favorites.getContentUri(item.id))
-                .withValues(mTempValues).build());
-    }
-
-    /**
-     * Tries the remove the provided row and column.
-     * @param items all the items on the screen under operation
-     * @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];
-
-        col = mShouldRemoveX ? col : Integer.MAX_VALUE;
-        row = mShouldRemoveY ? row : Integer.MAX_VALUE;
-
-        ArrayList<DbEntry> finalItems = new ArrayList<>();
-        ArrayList<DbEntry> removedItems = new ArrayList<>();
-
-        for (DbEntry item : items) {
-            if ((item.cellX <= col && (item.spanX + item.cellX) > col)
-                || (item.cellY <= row && (item.spanY + item.cellY) > row)) {
-                removedItems.add(item);
-                if (item.cellX >= col) item.cellX --;
-                if (item.cellY >= row) item.cellY --;
-            } else {
-                if (item.cellX > col) item.cellX --;
-                if (item.cellY > row) item.cellY --;
-                finalItems.add(item);
-                markCells(occupied, item, true);
-            }
-        }
-
-        OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied, removedItems);
-        placement.find();
-        finalItems.addAll(placement.finalPlacedItems);
-        outLoss[0] = placement.lowestWeightLoss;
-        outLoss[1] = placement.lowestMoveCost;
-        return finalItems;
-    }
-
-    @Thunk 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;
-            }
-        }
-    }
-
-    @Thunk 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;
-
-        // If set to true, item movement are not considered in move cost, leading to a more
-        // linear placement.
-        private final boolean ignoreMove;
-
-        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(boolean[][] occupied, ArrayList<DbEntry> itemsToPlace,
-                boolean ignoreMove) {
-            this.occupied = occupied;
-            this.itemsToPlace = itemsToPlace;
-            this.ignoreMove = ignoreMove;
-
-            // Sort the items such that larger widgets appear first followed by 1x1 items
-            Collections.sort(this.itemsToPlace);
-        }
-
-        public void find() {
-            find(0, 0, 0, new ArrayList<DbEntry>());
-        }
-
-        /**
-         * Recursively finds a placement for the provided items.
-         * @param index the position in {@link #itemsToPlace} to start looking at.
-         * @param weightLoss total weight loss upto this point
-         * @param moveCost total move cost upto this point
-         * @param itemsPlaced all the items already placed upto this point
-         */
-        public void find(int index, float weightLoss, float moveCost,
-                ArrayList<DbEntry> itemsPlaced) {
-            if ((weightLoss >= lowestWeightLoss) ||
-                    ((weightLoss == lowestWeightLoss) && (moveCost >= lowestMoveCost))) {
-                // Abort, as we already have a better solution.
-                return;
-
-            } else if (index >= itemsToPlace.size()) {
-                // End loop.
-                lowestWeightLoss = weightLoss;
-                lowestMoveCost = moveCost;
-
-                // Keep a deep copy of current configuration as it can change during recursion.
-                finalPlacedItems = deepCopy(itemsPlaced);
-                return;
-            }
-
-            DbEntry me = itemsToPlace.get(index);
-            int myX = me.cellX;
-            int myY = me.cellY;
-
-            // List of items to pass over if this item was placed.
-            ArrayList<DbEntry> itemsIncludingMe = new ArrayList<>(itemsPlaced.size() + 1);
-            itemsIncludingMe.addAll(itemsPlaced);
-            itemsIncludingMe.add(me);
-
-            if (me.spanX > 1 || me.spanY > 1) {
-                // If the current item is a widget (and it greater than 1x1), try to place it at
-                // all possible positions. This is because a widget placed at one position can
-                // affect the placement of a different widget.
-                int myW = me.spanX;
-                int myH = me.spanY;
-
-                for (int y = 0; y < mTrgY; y++) {
-                    for (int x = 0; x < mTrgX; x++) {
-                        float newMoveCost = moveCost;
-                        if (x != myX) {
-                            me.cellX = x;
-                            newMoveCost ++;
-                        }
-                        if (y != myY) {
-                            me.cellY = y;
-                            newMoveCost ++;
-                        }
-                        if (ignoreMove) {
-                            newMoveCost = moveCost;
-                        }
-
-                        if (isVacant(occupied, x, y, myW, myH)) {
-                            // place at this position and continue search.
-                            markCells(occupied, me, true);
-                            find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
-                            markCells(occupied, me, false);
-                        }
-
-                        // Try resizing horizontally
-                        if (myW > me.minSpanX && isVacant(occupied, x, y, myW - 1, myH)) {
-                            me.spanX --;
-                            markCells(occupied, me, true);
-                            // 1 extra move cost
-                            find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
-                            markCells(occupied, me, false);
-                            me.spanX ++;
-                        }
-
-                        // Try resizing vertically
-                        if (myH > me.minSpanY && isVacant(occupied, x, y, myW, myH - 1)) {
-                            me.spanY --;
-                            markCells(occupied, me, true);
-                            // 1 extra move cost
-                            find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
-                            markCells(occupied, me, false);
-                            me.spanY ++;
-                        }
-
-                        // Try resizing horizontally & vertically
-                        if (myH > me.minSpanY && myW > me.minSpanX &&
-                                isVacant(occupied, x, y, myW - 1, myH - 1)) {
-                            me.spanX --;
-                            me.spanY --;
-                            markCells(occupied, me, true);
-                            // 2 extra move cost
-                            find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe);
-                            markCells(occupied, me, false);
-                            me.spanX ++;
-                            me.spanY ++;
-                        }
-                        me.cellX = myX;
-                        me.cellY = myY;
-                    }
-                }
-
-                // Finally also try a solution when this item is not included. Trying it in the end
-                // causes it to get skipped in most cases due to higher weight loss, and prevents
-                // unnecessary deep copies of various configurations.
-                find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced);
-            } else {
-                // Since this is a 1x1 item and all the following items are also 1x1, just place
-                // it at 'the most appropriate position' and hope for the best.
-                // The most appropriate position: one with lease straight line distance
-                int newDistance = Integer.MAX_VALUE;
-                int newX = Integer.MAX_VALUE, newY = Integer.MAX_VALUE;
-
-                for (int y = 0; y < mTrgY; y++) {
-                    for (int x = 0; x < mTrgX; x++) {
-                        if (!occupied[x][y]) {
-                            int dist = ignoreMove ? 0 :
-                                ((me.cellX - x) * (me.cellX - x) + (me.cellY - y) * (me.cellY - y));
-                            if (dist < newDistance) {
-                                newX = x;
-                                newY = y;
-                                newDistance = dist;
-                            }
-                        }
-                    }
-                }
-
-                if (newX < mTrgX && newY < mTrgY) {
-                    float newMoveCost = moveCost;
-                    if (newX != myX) {
-                        me.cellX = newX;
-                        newMoveCost ++;
-                    }
-                    if (newY != myY) {
-                        me.cellY = newY;
-                        newMoveCost ++;
-                    }
-                    if (ignoreMove) {
-                        newMoveCost = moveCost;
-                    }
-                    markCells(occupied, me, true);
-                    find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
-                    markCells(occupied, me, false);
-                    me.cellX = myX;
-                    me.cellY = myY;
-
-                    // Try to find a solution without this item, only if
-                    //  1) there was at least one space, i.e., we were able to place this item
-                    //  2) if the next item has the same weight (all items are already sorted), as
-                    //     if it has lower weight, that solution will automatically get discarded.
-                    //  3) ignoreMove false otherwise, move cost is ignored and the weight will
-                    //      anyway be same.
-                    if (index + 1 < itemsToPlace.size()
-                            && itemsToPlace.get(index + 1).weight >= me.weight && !ignoreMove) {
-                        find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced);
-                    }
-                } else {
-                    // No more space. Jump to the end.
-                    for (int i = index + 1; i < itemsToPlace.size(); i++) {
-                        weightLoss += itemsToPlace.get(i).weight;
-                    }
-                    find(itemsToPlace.size(), weightLoss + me.weight, moveCost, itemsPlaced);
-                }
-            }
-        }
-    }
-
-    /**
-     * Loads entries for a particular screen id.
-     */
-    public ArrayList<DbEntry> loadEntries(long screen) {
-       Cursor c =  mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
-                new String[] {
-                    Favorites._ID,                  // 0
-                    Favorites.ITEM_TYPE,            // 1
-                    Favorites.CELLX,                // 2
-                    Favorites.CELLY,                // 3
-                    Favorites.SPANX,                // 4
-                    Favorites.SPANY,                // 5
-                    Favorites.INTENT,               // 6
-                    Favorites.APPWIDGET_PROVIDER},  // 7
-                Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP
-                    + " AND " + Favorites.SCREEN + " = " + screen, null, null, null);
-
-       final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
-       final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
-       final int indexCellX = c.getColumnIndexOrThrow(Favorites.CELLX);
-       final int indexCellY = c.getColumnIndexOrThrow(Favorites.CELLY);
-       final int indexSpanX = c.getColumnIndexOrThrow(Favorites.SPANX);
-       final int indexSpanY = c.getColumnIndexOrThrow(Favorites.SPANY);
-       final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT);
-       final int indexAppWidgetProvider = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER);
-
-       ArrayList<DbEntry> entries = new ArrayList<>();
-       while (c.moveToNext()) {
-           DbEntry entry = new DbEntry();
-           entry.id = c.getLong(indexId);
-           entry.itemType = c.getInt(indexItemType);
-           entry.cellX = c.getInt(indexCellX);
-           entry.cellY = c.getInt(indexCellY);
-           entry.spanX = c.getInt(indexSpanX);
-           entry.spanY = c.getInt(indexSpanY);
-           entry.screenId = screen;
-
-           try {
-               // calculate weight
-               switch (entry.itemType) {
-                   case Favorites.ITEM_TYPE_SHORTCUT:
-                   case Favorites.ITEM_TYPE_APPLICATION: {
-                       verifyIntent(c.getString(indexIntent));
-                       entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT
-                           ? WT_SHORTCUT : WT_APPLICATION;
-                       break;
-                   }
-                   case Favorites.ITEM_TYPE_APPWIDGET: {
-                       String provider = c.getString(indexAppWidgetProvider);
-                       ComponentName cn = ComponentName.unflattenFromString(provider);
-                       verifyPackage(cn.getPackageName());
-                       entry.weight = Math.max(WT_WIDGET_MIN, WT_WIDGET_FACTOR
-                               * entry.spanX * entry.spanY);
-
-                       // Migration happens for current user only.
-                       LauncherAppWidgetProviderInfo pInfo = LauncherModel.getProviderInfo(
-                               mContext, cn, UserHandleCompat.myUserHandle());
-                       Point spans = pInfo == null ?
-                               mWidgetMinSize.get(provider) : pInfo.getMinSpans(mIdp, mContext);
-                       if (spans != null) {
-                           entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX;
-                           entry.minSpanY = spans.y > 0 ? spans.y : entry.spanY;
-                       } else {
-                           // Assume that the widget be resized down to 2x2
-                           entry.minSpanX = entry.minSpanY = 2;
-                       }
-
-                       if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) {
-                           throw new Exception("Widget can't be resized down to fit the grid");
-                       }
-                       break;
-                   }
-                   case Favorites.ITEM_TYPE_FOLDER: {
-                       int total = getFolderItemsCount(entry.id);
-                       if (total == 0) {
-                           throw new Exception("Folder is empty");
-                       }
-                       entry.weight = WT_FOLDER_FACTOR * total;
-                       break;
-                   }
-                   default:
-                       throw new Exception("Invalid item type");
-               }
-           } catch (Exception e) {
-               if (DEBUG) {
-                   Log.d(TAG, "Removing item " + entry.id, e);
-               }
-               mEntryToRemove.add(entry.id);
-               continue;
-           }
-           entries.add(entry);
-       }
-       c.close();
-       return entries;
-    }
-
-    /**
-     * @return the number of valid items in the folder.
-     */
-    private int getFolderItemsCount(long folderId) {
-        Cursor c =  mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
-                new String[] {Favorites._ID, Favorites.INTENT},
-                Favorites.CONTAINER + " = " + folderId, null, null, null);
-
-        int total = 0;
-        while (c.moveToNext()) {
-            try {
-                verifyIntent(c.getString(1));
-                total++;
-            } catch (Exception e) {
-                mEntryToRemove.add(c.getLong(0));
-            }
-        }
-        c.close();
-        return total;
-    }
-
-    /**
-     * Verifies if the intent should be restored.
-     */
-    private void verifyIntent(String intentStr) throws Exception {
-        Intent intent = Intent.parseUri(intentStr, 0);
-        if (intent.getComponent() != null) {
-            verifyPackage(intent.getComponent().getPackageName());
-        } else if (intent.getPackage() != null) {
-            // Only verify package if the component was null.
-            verifyPackage(intent.getPackage());
-        }
-    }
-
-    /**
-     * Verifies if the package should be restored
-     */
-    private void verifyPackage(String packageName) throws Exception {
-        if (!mValidPackages.contains(packageName)) {
-            throw new Exception("Package not available");
-        }
-    }
-
-    private static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
-
-        public float weight;
-
-        public DbEntry() { }
-
-        public DbEntry copy() {
-            DbEntry entry = new DbEntry();
-            entry.copyFrom(this);
-            entry.weight = weight;
-            entry.minSpanX = minSpanX;
-            entry.minSpanY = minSpanY;
-            return entry;
-        }
-
-        /**
-         * Comparator such that larger widgets come first,  followed by all 1x1 items
-         * based on their weights.
-         */
-        @Override
-        public int compareTo(DbEntry another) {
-            if (itemType == Favorites.ITEM_TYPE_APPWIDGET) {
-                if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
-                    return another.spanY * another.spanX - spanX * spanY;
-                } else {
-                    return -1;
-                }
-            } else if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
-                return 1;
-            } else {
-                // Place higher weight before lower weight.
-                return Float.compare(another.weight, weight);
-            }
-        }
-
-        public boolean columnsSame(DbEntry org) {
-            return org.cellX == cellX && org.cellY == cellY && org.spanX == spanX &&
-                    org.spanY == spanY && org.screenId == screenId;
-        }
-
-        public void addToContentValues(ContentValues values) {
-            values.put(LauncherSettings.Favorites.SCREEN, screenId);
-            values.put(LauncherSettings.Favorites.CELLX, cellX);
-            values.put(LauncherSettings.Favorites.CELLY, cellY);
-            values.put(LauncherSettings.Favorites.SPANX, spanX);
-            values.put(LauncherSettings.Favorites.SPANY, spanY);
-        }
-    }
-
-    @Thunk static ArrayList<DbEntry> deepCopy(ArrayList<DbEntry> src) {
-        ArrayList<DbEntry> dup = new ArrayList<DbEntry>(src.size());
-        for (DbEntry e : src) {
-            dup.add(e.copy());
-        }
-        return dup;
-    }
-
-    private static Point parsePoint(String point) {
-        String[] split = point.split(",");
-        return new Point(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
-    }
-
-    public static void markForMigration(Context context, int srcX, int srcY,
-            HashSet<String> widgets) {
-        Utilities.getPrefs(context).edit()
-                .putString(KEY_MIGRATION_SOURCE_SIZE, srcX + "," + srcY)
-                .putStringSet(KEY_MIGRATION_WIDGET_MINSIZE, widgets)
-                .apply();
-    }
-
-    public static boolean shouldRunTask(Context context) {
-        return !TextUtils.isEmpty(Utilities.getPrefs(context).getString(KEY_MIGRATION_SOURCE_SIZE, ""));
-    }
-
-    public static void clearFlags(Context context) {
-        Utilities.getPrefs(context).edit().remove(KEY_MIGRATION_SOURCE_SIZE)
-                .remove(KEY_MIGRATION_WIDGET_MINSIZE).commit();
-    }
-
-}
diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
index 30f228c..ddc9cbf 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.
  */
@@ -59,7 +57,6 @@
         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 + ")";
+                + " spanX=" + spanX + " spanY=" + spanY + " user=" + user + ")";
     }
 }
diff --git a/src/com/android/launcher3/model/WidgetItem.java b/src/com/android/launcher3/model/WidgetItem.java
new file mode 100644
index 0000000..b3f0c82
--- /dev/null
+++ b/src/com/android/launcher3/model/WidgetItem.java
@@ -0,0 +1,73 @@
+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;
+        }
+
+        return sCollator.compare(label, another.label);
+    }
+}
diff --git a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
deleted file mode 100644
index 2e4167e..0000000
--- a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
+++ /dev/null
@@ -1,105 +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 android.util.Log;
-
-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;
-            try {
-                return Utilities.trim(shortcutInfo.loadLabel(mPackageManager));
-            } catch (Exception e) {
-                Log.e("ShortcutNameComparator",
-                        "Failed to extract app display name from resolve info", e);
-                return "";
-            }
-        }
-    }
-};
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 99a53ff..b2a94bb 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -1,9 +1,13 @@
 
 package com.android.launcher3.model;
 
-import android.content.ComponentName;
+import android.appwidget.AppWidgetProviderInfo;
 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;
 import android.util.Log;
 
 import com.android.launcher3.AppFilter;
@@ -12,10 +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;
@@ -37,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;
@@ -65,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.
@@ -87,16 +88,54 @@
         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;
     }
 
-    public void setWidgetsAndShortcuts(ArrayList<Object> rawWidgetsShortcuts) {
-        Utilities.assertWorkerThread();
+    public boolean isEmpty() {
+        return mRawList.isEmpty();
+    }
+
+    public WidgetsModel updateAndClone(Context context) {
+        Preconditions.assertWorkerThread();
+
+        try {
+            final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
+            // Widgets
+            AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(context);
+            for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders()) {
+                widgetsAndShortcuts.add(new WidgetItem(
+                        LauncherAppWidgetProviderInfo.fromProviderInfo(context, widgetInfo),
+                        widgetManager));
+            }
+
+            // Shortcuts
+            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 (!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
+                // time Launcher starts.
+                // TODO: after figuring out a repro step, introduce a dirty bit to check when
+                // onResume is called to refresh the widget provider list.
+            } else {
+                throw e;
+            }
+        }
+        return clone();
+    }
+
+    private void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts) {
         mRawList = rawWidgetsShortcuts;
         if (DEBUG) {
             Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
@@ -109,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/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index 1bb5787..c62ce0e 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -1,25 +1,18 @@
 package com.android.launcher3.testing;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.view.Menu;
 import android.view.View;
-import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
 import com.android.launcher3.AppInfo;
-import com.android.launcher3.InsettableFrameLayout;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherCallbacks;
-import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsSearchBarController;
+import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.util.ComponentKey;
 
 import java.io.FileDescriptor;
@@ -42,8 +35,6 @@
 
     public class LauncherExtensionCallbacks implements LauncherCallbacks {
 
-        LauncherExtensionOverlay mLauncherOverlay = new LauncherExtensionOverlay();
-
         @Override
         public void preOnCreate() {
         }
@@ -116,10 +107,6 @@
 
         @Override
         public boolean handleBackPressed() {
-            if (mLauncherOverlay.isOverlayPanelShowing()) {
-                mLauncherOverlay.hideOverlayPanel();
-                return true;
-            }
             return false;
         }
 
@@ -136,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() {
         }
 
@@ -188,13 +139,8 @@
         }
 
         @Override
-        public boolean forceDisableVoiceButtonProxy() {
-            return false;
-        }
-
-        @Override
         public boolean providesSearch() {
-            return true;
+            return false;
         }
 
         @Override
@@ -203,15 +149,6 @@
             return false;
         }
 
-        @Override
-        public boolean startSearchFromAllApps(String query) {
-            return false;
-        }
-
-        @Override
-        public void startVoice() {
-        }
-
         CustomContentCallbacks mCustomContentCallbacks = new CustomContentCallbacks() {
 
             // Custom content is completely shown. {@code fromResume} indicates whether this was caused
@@ -248,8 +185,11 @@
         }
 
         @Override
+        public UserEventDispatcher getUserEventDispatcher() { return null; }
+
+        @Override
         public View getQsbBar() {
-            return mLauncherOverlay.getSearchBox();
+            return null;
         }
 
         @Override
@@ -288,22 +228,13 @@
         }
 
         @Override
-        public ComponentName getWallpaperPickerComponent() {
-            return null;
-        }
-
-        @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<>();
         }
 
@@ -313,118 +244,16 @@
         }
 
         @Override
-        public boolean isLauncherPreinstalled() {
-            return false;
-        }
-
-        @Override
-        public boolean hasLauncherOverlay() {
-            return false;
-        }
-
-        @Override
-        public LauncherOverlay setLauncherOverlayView(InsettableFrameLayout container,
-                LauncherOverlayCallbacks callbacks) {
-
-            mLauncherOverlay.setOverlayCallbacks(callbacks);
-            mLauncherOverlay.setOverlayContainer(container);
-
-            return mLauncherOverlay;
-        }
-
-        @Override
         public void setLauncherSearchCallback(Object callbacks) {
             // Do nothing
         }
 
-        class LauncherExtensionOverlay implements LauncherOverlay {
-            LauncherOverlayCallbacks mLauncherOverlayCallbacks;
-            ViewGroup mOverlayView;
-            View mSearchBox;
-            View mSearchOverlay;
-            boolean mShowOverlayFeedback;
-            int mProgress;
-            boolean mOverlayPanelShowing;
+        @Override
+        public void onAttachedToWindow() {
+        }
 
-            @Override
-            public void onScrollInteractionBegin() {
-                if (mLauncherOverlayCallbacks.canEnterFullImmersion()) {
-                    mShowOverlayFeedback = true;
-                    updatePanelOffset(0);
-                    mSearchOverlay.setVisibility(View.VISIBLE);
-                    mSearchOverlay.setLayerType(View.LAYER_TYPE_HARDWARE, null);
-                }
-            }
-
-            @Override
-            public void onScrollChange(int progress, boolean rtl) {
-                mProgress = progress;
-                if (mShowOverlayFeedback) {
-                    updatePanelOffset(progress);
-                }
-            }
-
-            private void updatePanelOffset(int progress) {
-                int panelWidth = mSearchOverlay.getMeasuredWidth();
-                int offset = (int) ((progress / 100f) * panelWidth);
-                mSearchOverlay.setTranslationX(- panelWidth + offset);
-            }
-
-            @Override
-            public void onScrollInteractionEnd() {
-                if (mProgress > 25 && mLauncherOverlayCallbacks.enterFullImmersion()) {
-                    ObjectAnimator oa = LauncherAnimUtils.ofFloat(mSearchOverlay, "translationX", 0);
-                    oa.addListener(new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator arg0) {
-                            mSearchOverlay.setLayerType(View.LAYER_TYPE_NONE, null);
-                        }
-                    });
-                    oa.start();
-                    mOverlayPanelShowing = true;
-                    mShowOverlayFeedback = false;
-                }
-            }
-
-            @Override
-            public void onScrollSettled() {
-                if (mShowOverlayFeedback) {
-                    mSearchOverlay.setVisibility(View.INVISIBLE);
-                    mSearchOverlay.setLayerType(View.LAYER_TYPE_NONE, null);
-                }
-                mShowOverlayFeedback = false;
-                mProgress = 0;
-            }
-
-            public void hideOverlayPanel() {
-                mLauncherOverlayCallbacks.exitFullImmersion();
-                mSearchOverlay.setVisibility(View.INVISIBLE);
-                mOverlayPanelShowing = false;
-            }
-
-            public boolean isOverlayPanelShowing() {
-                return mOverlayPanelShowing;
-            }
-
-            @Override
-            public void forceExitFullImmersion() {
-                hideOverlayPanel();
-            }
-
-            public void setOverlayContainer(InsettableFrameLayout container) {
-                mOverlayView = (ViewGroup) getLayoutInflater().inflate(
-                        R.layout.launcher_overlay_example, container);
-                mSearchOverlay = mOverlayView.findViewById(R.id.search_overlay);
-                mSearchBox = mOverlayView.findViewById(R.id.search_box);
-            }
-
-            public View getSearchBox() {
-                return mSearchBox;
-            }
-
-            public void setOverlayCallbacks(LauncherOverlayCallbacks callbacks) {
-                mLauncherOverlayCallbacks = callbacks;
-            }
-        };
+        @Override
+        public void onDetachedFromWindow() {
+        }
     }
 }
diff --git a/src/com/android/launcher3/util/CachedPackageTracker.java b/src/com/android/launcher3/util/CachedPackageTracker.java
new file mode 100644
index 0000000..d55d573
--- /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);
+
+    protected 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/ConfigMonitor.java b/src/com/android/launcher3/util/ConfigMonitor.java
index c61fa88..bdb1639 100644
--- a/src/com/android/launcher3/util/ConfigMonitor.java
+++ b/src/com/android/launcher3/util/ConfigMonitor.java
@@ -23,26 +23,30 @@
 import android.content.res.Configuration;
 import android.util.Log;
 
+import com.android.launcher3.Utilities;
+
 /**
  * {@link BroadcastReceiver} which watches configuration changes and
- * restarts the process in case changes which affect the device profile.
+ * restarts the process in case changes which affect the device profile occur.
  */
 public class ConfigMonitor extends BroadcastReceiver {
 
     private final Context mContext;
     private final float mFontScale;
+    private final int mDensity;
 
     public ConfigMonitor(Context context) {
         mContext = context;
 
         Configuration config = context.getResources().getConfiguration();
         mFontScale = config.fontScale;
+        mDensity = getDensity(config);
     }
 
     @Override
     public void onReceive(Context context, Intent intent) {
         Configuration config = context.getResources().getConfiguration();
-        if (mFontScale != config.fontScale) {
+        if (mFontScale != config.fontScale || mDensity != getDensity(config)) {
             Log.d("ConfigMonitor", "Configuration changed, restarting launcher");
             mContext.unregisterReceiver(this);
             android.os.Process.killProcess(android.os.Process.myPid());
@@ -52,4 +56,8 @@
     public void register() {
         mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
     }
+
+    private static int getDensity(Configuration config) {
+        return Utilities.ATLEAST_JB_MR1 ? config.densityDpi : 0;
+    }
 }
diff --git a/src/com/android/launcher3/util/CursorIconInfo.java b/src/com/android/launcher3/util/CursorIconInfo.java
index cdf9e3c..120eacd 100644
--- a/src/com/android/launcher3/util/CursorIconInfo.java
+++ b/src/com/android/launcher3/util/CursorIconInfo.java
@@ -30,13 +30,11 @@
  * 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);
         iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
         iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
         iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
@@ -44,26 +42,17 @@
 
     public Bitmap loadIcon(Cursor c, ShortcutInfo info, Context context) {
         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:
+        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);
-            info.customIcon = icon != null;
-            break;
         }
         return icon;
     }
diff --git a/src/com/android/launcher3/util/FlagOp.java b/src/com/android/launcher3/util/FlagOp.java
new file mode 100644
index 0000000..5e26ed1
--- /dev/null
+++ b/src/com/android/launcher3/util/FlagOp.java
@@ -0,0 +1,30 @@
+package com.android.launcher3.util;
+
+public abstract class FlagOp {
+
+    public static FlagOp NO_OP = new FlagOp() {};
+
+    private FlagOp() {}
+
+    public int apply(int flags) {
+        return flags;
+    }
+
+    public static FlagOp addFlag(final int flag) {
+        return new FlagOp() {
+            @Override
+            public int apply(int flags) {
+                return flags | flag;
+            }
+        };
+    }
+
+    public static FlagOp removeFlag(final int flag) {
+        return new FlagOp() {
+            @Override
+            public int apply(int flags) {
+                return flags & ~flag;
+            }
+        };
+    }
+}
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/IconNormalizer.java b/src/com/android/launcher3/util/IconNormalizer.java
index 001cac0..4087d7b 100644
--- a/src/com/android/launcher3/util/IconNormalizer.java
+++ b/src/com/android/launcher3/util/IconNormalizer.java
@@ -28,7 +28,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;
 
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index fb9bbb2..df23abe 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,19 @@
 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.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 +62,137 @@
     }
 
     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 = ShortcutInfo.fromActivityInfo(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);
         }
     }
 
     /**
      * 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/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
new file mode 100644
index 0000000..08e8e86
--- /dev/null
+++ b/src/com/android/launcher3/util/PackageManagerHelper.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.util;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * Utility methods using package manager
+ */
+public class PackageManagerHelper {
+
+    private static final int FLAG_SUSPENDED = 1<<30;
+
+    /**
+     * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't
+     * guarantee that the app is on SD card.
+     */
+    public static boolean isAppOnSdcard(PackageManager pm, String packageName) {
+        return isAppEnabled(pm, packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
+    }
+
+    public static boolean isAppEnabled(PackageManager pm, String packageName) {
+        return isAppEnabled(pm, packageName, 0);
+    }
+
+    public static boolean isAppEnabled(PackageManager pm, String packageName, int flags) {
+        try {
+            ApplicationInfo info = pm.getApplicationInfo(packageName, flags);
+            return info != null && info.enabled;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    public static boolean isAppSuspended(PackageManager pm, String packageName) {
+        try {
+            ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+            return info != null && isAppSuspended(info);
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    public static boolean isAppSuspended(ApplicationInfo info) {
+        // The value of FLAG_SUSPENDED was reused by a hidden constant
+        // ApplicationInfo.FLAG_PRIVILEGED prior to N, so only check for suspended flag on N
+        // or later.
+        if (Utilities.isNycOrAbove()) {
+            return (info.flags & FLAG_SUSPENDED) != 0;
+        } else {
+            return false;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/util/Preconditions.java b/src/com/android/launcher3/util/Preconditions.java
new file mode 100644
index 0000000..3760c63
--- /dev/null
+++ b/src/com/android/launcher3/util/Preconditions.java
@@ -0,0 +1,50 @@
+/*
+ * 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 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/SQLiteCacheHelper.java b/src/com/android/launcher3/util/SQLiteCacheHelper.java
index 62a30d0..c455791 100644
--- a/src/com/android/launcher3/util/SQLiteCacheHelper.java
+++ b/src/com/android/launcher3/util/SQLiteCacheHelper.java
@@ -98,7 +98,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/StringFilter.java b/src/com/android/launcher3/util/StringFilter.java
new file mode 100644
index 0000000..f539ad1
--- /dev/null
+++ b/src/com/android/launcher3/util/StringFilter.java
@@ -0,0 +1,31 @@
+package com.android.launcher3.util;
+
+import java.util.Set;
+
+/**
+ * Abstract class to filter a set of strings.
+ */
+public abstract class StringFilter {
+
+    private StringFilter() { }
+
+    public abstract boolean matches(String str);
+
+    public static StringFilter matchesAll() {
+        return new StringFilter() {
+            @Override
+            public boolean matches(String str) {
+                return true;
+            }
+        };
+    }
+
+    public static StringFilter of(final Set<String> validEntries) {
+        return new StringFilter() {
+            @Override
+            public boolean matches(String str) {
+                return validEntries.contains(str);
+            }
+        };
+    }
+}
diff --git a/src/com/android/launcher3/util/ViewOnDrawExecutor.java b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
new file mode 100644
index 0000000..01808ba
--- /dev/null
+++ b/src/com/android/launcher3/util/ViewOnDrawExecutor.java
@@ -0,0 +1,96 @@
+/**
+ * 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.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;
+
+    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() {
+        mAttachedView.post(this);
+    }
+
+    @Override
+    public void run() {
+        for (final Runnable r : mTasks) {
+            mHandler.post(r);
+        }
+        markCompleted();
+    }
+
+    public void markCompleted() {
+        mTasks.clear();
+        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..1fbd0dc
--- /dev/null
+++ b/src/com/android/launcher3/util/WallpaperOffsetInterpolator.java
@@ -0,0 +1,225 @@
+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 = 3;
+
+    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 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);
+                }
+            }
+        }
+    }
+
+    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 (mWorkspace.getChildCount() <= 1) {
+            if (mIsRtl) {
+                return 1 - 1.0f/mNumPagesForWallpaperParallax;
+            }
+            return 0;
+        }
+
+        // Exclude the leftmost page
+        int emptyExtraPages = numEmptyScreensToIgnore();
+        int firstIndex = mWorkspace.numCustomPages();
+        // Exclude the last extra empty screen (if we have > MIN_PARALLAX_PAGE_SPAN pages)
+        int lastIndex = mWorkspace.getChildCount() - 1 - emptyExtraPages;
+        if (mIsRtl) {
+            int temp = firstIndex;
+            firstIndex = lastIndex;
+            lastIndex = temp;
+        }
+
+        int firstPageScrollX = mWorkspace.getScrollForPage(firstIndex);
+        int scrollRange = mWorkspace.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 - mWorkspace.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(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;
+        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;
+    }
+
+    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/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index fcb714f..de06ab6 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;
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 94bbd92..9ec0340 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);
@@ -93,9 +89,8 @@
 
         final Resources r = context.getResources();
         mLauncher = (Launcher) context;
-        mStylusEventHelper = new StylusEventHelper(this);
+        mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
 
-        mDimensionsFormatString = r.getString(R.string.widget_dims_format);
         setContainerWidth();
         setWillNotDraw(false);
         setClipToPadding(false);
@@ -135,33 +130,18 @@
         }
     }
 
-    /**
-     * 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));
         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 +170,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 +191,7 @@
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         boolean handled = super.onTouchEvent(ev);
-        if (mStylusEventHelper.checkAndPerformStylusEvent(ev)) {
+        if (mStylusEventHelper.onMotionEvent(ev)) {
             return true;
         }
         return handled;
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 5d3af52..297505b 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -10,13 +10,14 @@
 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.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.util.Thunk;
 
 public class WidgetHostViewLoader implements DragController.DragListener {
@@ -46,7 +47,7 @@
     }
 
     @Override
-    public void onDragStart(DragSource source, Object info, int dragAction) { }
+    public void onDragStart(DragSource source, ItemInfo info, int dragAction) { }
 
     @Override
     public void onDragEnd() {
@@ -150,15 +151,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 10a00c6..23d0433 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -20,6 +20,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView.State;
 import android.util.AttributeSet;
@@ -31,10 +32,9 @@
 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.folder.Folder;
 import com.android.launcher3.IconCache;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
@@ -44,6 +44,7 @@
 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.util.Thunk;
 
@@ -82,7 +83,7 @@
         super(context, attrs, defStyleAttr);
         mLauncher = (Launcher) context;
         mDragController = mLauncher.getDragController();
-        mAdapter = new WidgetsListAdapter(context, this, this, mLauncher);
+        mAdapter = new WidgetsListAdapter(this, this, mLauncher);
         mIconCache = (LauncherAppState.getInstance()).getIconCache();
         if (LOGD) {
             Log.d(TAG, "WidgetsContainerView constructor");
@@ -95,6 +96,18 @@
         mRecyclerView = (WidgetsRecyclerView) getContentView().findViewById(R.id.widgets_list_view);
         mRecyclerView.setAdapter(mAdapter);
         mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+
+        Rect bgPadding = new Rect();
+        getRevealView().getBackground().getPadding(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));
+        }
     }
 
     //
@@ -227,9 +240,11 @@
 
         // Start the drag
         mLauncher.lockScreenOrientation();
-        mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, preview, clipAlpha);
         mDragController.startDrag(image, preview, this, createItemInfo,
                 bounds, DragController.DRAG_ACTION_COPY, scale);
+        // This call expects the extra empty screen to already be created, which is why we call it
+        // after mDragController.startDrag().
+        mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, preview, clipAlpha);
 
         preview.recycle();
         return true;
@@ -241,7 +256,7 @@
 
     @Override
     public boolean supportsFlingToDelete() {
-        return false;
+        return true;
     }
 
     @Override
@@ -294,7 +309,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 +322,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.
      */
@@ -332,6 +331,10 @@
         mAdapter.notifyDataSetChanged();
     }
 
+    public boolean isEmpty() {
+        return mAdapter.getItemCount() == 0;
+    }
+
     private WidgetPreviewLoader getWidgetPreviewLoader() {
         if (mWidgetPreviewLoader == null) {
             mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index 885d96f..de966f9 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -16,9 +16,6 @@
 package com.android.launcher3.widget;
 
 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 +27,13 @@
 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;
@@ -64,20 +60,17 @@
     private View.OnClickListener mIconClickListener;
     private View.OnLongClickListener mIconLongClickListener;
 
-    private static final int PRESET_INDENT_SIZE_TABLET = 56;
-    private int mIndent = 0;
+    private final int mIndent;
 
-    public WidgetsListAdapter(Context context,
-            View.OnClickListener iconClickListener,
+    public WidgetsListAdapter(View.OnClickListener iconClickListener,
             View.OnLongClickListener iconLongClickListener,
             Launcher launcher) {
-        mLayoutInflater = LayoutInflater.from(context);
+        mLayoutInflater = launcher.getLayoutInflater();
 
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
         mLauncher = launcher;
-
-        setContainerHeight();
+        mIndent = launcher.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
     }
 
     public void setWidgetsModel(WidgetsModel w) {
@@ -94,7 +87,7 @@
 
     @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));
         if (DEBUG) {
@@ -139,17 +132,7 @@
         }
         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);
         }
@@ -206,12 +189,4 @@
         }
         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..9c13b44 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;
 
@@ -58,6 +57,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) {
diff --git a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
index 249559a..19bc868 100644
--- a/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
+++ b/src/com/android/launcher3/widget/WidgetsRowViewHolder.java
@@ -16,10 +16,7 @@
 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;
 
 public class WidgetsRowViewHolder extends ViewHolder {
 
diff --git a/tests/Android.mk b/tests/Android.mk
index d82f0b3..0c4b5ff 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -16,13 +16,10 @@
 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
 
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..34b1174
--- /dev/null
+++ b/tests/src/com/android/launcher3/BindWidgetTest.java
@@ -0,0 +1,431 @@
+package com.android.launcher3;
+
+import android.annotation.TargetApi;
+import android.app.SearchManager;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+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.os.ParcelFileDescriptor;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiSelector;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.widget.PendingAddWidgetInfo;
+import com.android.launcher3.widget.WidgetHostViewLoader;
+
+import java.io.FileInputStream;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * 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 InstrumentationTestCase {
+
+    private static final long DEFAULT_TIMEOUT = 6000;
+
+    private UiDevice mDevice;
+    private Context mTargetContext;
+    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();
+
+        mDevice = UiDevice.getInstance(getInstrumentation());
+        mTargetContext = getInstrumentation().getTargetContext();
+        mResolver = mTargetContext.getContentResolver();
+        mWidgetManager = AppWidgetManagerCompat.getInstance(mTargetContext);
+
+        // Check bind widget permission
+        String pkg = mTargetContext.getPackageName();
+        if (mTargetContext.getPackageManager().checkPermission(
+                pkg, android.Manifest.permission.BIND_APPWIDGET)
+                != PackageManager.PERMISSION_GRANTED) {
+            ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand(
+                    "appwidget grantbind --package " + pkg);
+            // Read the input stream fully.
+            FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+            while (fis.read() != -1);
+            fis.close();
+        }
+
+        // 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) {
+        // Add new screen
+        long screenId = LauncherSettings.Settings.call(
+                mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
+                .getLong(LauncherSettings.Settings.EXTRA_VALUE);
+        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
+        getInstrumentation().getContext().startActivity(new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setPackage(mTargetContext.getPackageName())
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+
+        // Verify UI
+        UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
+                .className(widgetClass);
+        if (desc != null) {
+            selector = selector.description(desc);
+        }
+        assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_TIMEOUT));
+    }
+
+    /**
+     * Finds a widget provider which can fit on the home screen.
+     * @param hasConfigureScreen if true, a provider with a config screen is returned.
+     */
+    private 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;
+    }
+
+    /**
+     * 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 = 0;
+        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 = 0;
+        item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
+        return item;
+    }
+
+    /**
+     * Runs the callback on the UI thread and returns the result.
+     */
+    private <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();
+    }
+
+    /**
+     * 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..476eb0a 100644
--- a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
+++ b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
@@ -168,7 +168,9 @@
                 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);
+            // TODO: Remove the comparision against 101 once b/26884580 is fixed
+            // assertEquals(4 + 94 + 2, portraitHeight);
+            assertTrue(portraitHeight == (4 + 94 + 2) || portraitHeight == (4 + 95 + 2));
         } else {
             assertEquals(8 + 94 + 24, portraitHeight);
         }
diff --git a/tests/src/com/android/launcher3/LauncherBackupAgentTest.java b/tests/src/com/android/launcher3/LauncherBackupAgentTest.java
new file mode 100644
index 0000000..020a557
--- /dev/null
+++ b/tests/src/com/android/launcher3/LauncherBackupAgentTest.java
@@ -0,0 +1,74 @@
+package com.android.launcher3;
+
+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 LauncherBackupAgent}
+ */
+@MediumTest
+public class LauncherBackupAgentTest extends AndroidTestCase {
+
+    public void testGetProfileId() throws Exception {
+        SQLiteDatabase db = new MyDatabaseHelper(23).getWritableDatabase();
+        assertEquals(23, new LauncherBackupAgent().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 LauncherBackupAgent().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/QuickAddWidgetTest.java b/tests/src/com/android/launcher3/QuickAddWidgetTest.java
index 027c465..0078294 100644
--- a/tests/src/com/android/launcher3/QuickAddWidgetTest.java
+++ b/tests/src/com/android/launcher3/QuickAddWidgetTest.java
@@ -9,6 +9,7 @@
 import android.support.test.uiautomator.UiObject2;
 import android.support.test.uiautomator.Until;
 import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 
@@ -17,6 +18,7 @@
 /**
  * Add an arbitrary widget from the widget picker very quickly to test potential race conditions.
  */
+@LargeTest
 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.
diff --git a/tests/src/com/android/launcher3/RotationPreferenceTest.java b/tests/src/com/android/launcher3/RotationPreferenceTest.java
index 0168ee6..7259d35 100644
--- a/tests/src/com/android/launcher3/RotationPreferenceTest.java
+++ b/tests/src/com/android/launcher3/RotationPreferenceTest.java
@@ -8,10 +8,12 @@
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiSelector;
 import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
 
 /**
  * Test for auto rotate preference.
  */
+@MediumTest
 public class RotationPreferenceTest extends InstrumentationTestCase {
 
     private UiDevice mDevice;
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
new file mode 100644
index 0000000..ccd8f73
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -0,0 +1,323 @@
+package com.android.launcher3.model;
+
+import android.content.ContentValues;
+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.LauncherModel;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.util.TestLauncherProvider;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * Unit tests for {@link GridSizeMigrationTask}
+ */
+@MediumTest
+public class GridSizeMigrationTaskTest extends ProviderTestCase2<TestLauncherProvider> {
+
+    private static final long DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP;
+    private static final long HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+
+    private static final int APPLICATION = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+    private static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+
+    private static final String TEST_PACKAGE = "com.android.launcher3.validpackage";
+    private static final String VALID_INTENT =
+            new Intent(Intent.ACTION_MAIN).setPackage(TEST_PACKAGE).toUri(0);
+
+    private HashSet<String> mValidPackages;
+    private InvariantDeviceProfile mIdp;
+
+    public GridSizeMigrationTaskTest() {
+        super(TestLauncherProvider.class, ProviderConfig.AUTHORITY);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mValidPackages = new HashSet<>();
+        mValidPackages.add(TEST_PACKAGE);
+
+        mIdp = new InvariantDeviceProfile();
+    }
+
+    public void testHotseatMigration_apps_dropped() throws Exception {
+        long[] hotseatItems = {
+                addItem(APPLICATION, 0, HOTSEAT, 0, 0),
+                addItem(SHORTCUT, 1, HOTSEAT, 0, 0),
+                -1,
+                addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
+                addItem(APPLICATION, 4, HOTSEAT, 0, 0),
+        };
+
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 2, 3, 1)
+                .migrateHotseat();
+        // First & last items are dropped as they have the least weight.
+        verifyHotseat(hotseatItems[1], -1, hotseatItems[3]);
+    }
+
+    public void testHotseatMigration_shortcuts_dropped() throws Exception {
+        long[] hotseatItems = {
+                addItem(APPLICATION, 0, HOTSEAT, 0, 0),
+                addItem(30, 1, HOTSEAT, 0, 0),
+                -1,
+                addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
+                addItem(10, 4, HOTSEAT, 0, 0),
+        };
+
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 2, 3, 1)
+                .migrateHotseat();
+        // First & third items are dropped as they have the least weight.
+        verifyHotseat(hotseatItems[1], -1, hotseatItems[4]);
+    }
+
+    private void verifyHotseat(long... sortedIds) {
+        int screenId = 0;
+        int total = 0;
+
+        for (long id : sortedIds) {
+            Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                    new String[]{LauncherSettings.Favorites._ID},
+                    "container=-101 and screen=" + screenId, null, null, null);
+
+            if (id == -1) {
+                assertEquals(0, c.getCount());
+            } else {
+                assertEquals(1, c.getCount());
+                c.moveToNext();
+                assertEquals(id, c.getLong(0));
+                total ++;
+            }
+            c.close();
+
+            screenId++;
+        }
+
+        // Verify that not other entry exist in the DB.
+        Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                new String[]{LauncherSettings.Favorites._ID},
+                "container=-101", null, null, null);
+        assertEquals(total, c.getCount());
+        c.close();
+    }
+
+    public void testWorkspace_empty_row_column_removed() throws Exception {
+        long[][][] ids = createGrid(new int[][][]{{
+                {  0,  0, -1,  1},
+                {  3,  1, -1,  4},
+                { -1, -1, -1, -1},
+                {  5,  2, -1,  6},
+        }});
+
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+        // Column 2 and row 2 got removed.
+        verifyWorkspace(new long[][][] {{
+                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
+                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
+                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
+        }});
+    }
+
+    public void testWorkspace_new_screen_created() throws Exception {
+        long[][][] ids = createGrid(new int[][][]{{
+                {  0,  0,  0,  1},
+                {  3,  1,  0,  4},
+                { -1, -1, -1, -1},
+                {  5,  2, -1,  6},
+        }});
+
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+        // Items in the second column get moved to new screen
+        verifyWorkspace(new long[][][] {{
+                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
+                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
+                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
+        }, {
+                {ids[0][0][2], ids[0][1][2], -1},
+        }});
+    }
+
+    public void testWorkspace_items_merged_in_next_screen() throws Exception {
+        long[][][] ids = createGrid(new int[][][]{{
+                {  0,  0,  0,  1},
+                {  3,  1,  0,  4},
+                { -1, -1, -1, -1},
+                {  5,  2, -1,  6},
+        },{
+                {  0,  0, -1,  1},
+                {  3,  1, -1,  4},
+        }});
+
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+                new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+        // Items in the second column of the first screen should get placed on the 3rd
+        // row of the second screen
+        verifyWorkspace(new long[][][] {{
+                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
+                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
+                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
+        }, {
+                {ids[1][0][0], ids[1][0][1], ids[1][0][3]},
+                {ids[1][1][0], ids[1][1][1], ids[1][1][3]},
+                {ids[0][0][2], ids[0][1][2], -1},
+        }});
+    }
+
+    public void testWorkspace_items_not_merged_in_next_screen() throws Exception {
+        // First screen has 2 items that need to be moved, but second screen has only one
+        // empty space after migration (top-left corner)
+        long[][][] ids = createGrid(new int[][][]{{
+                {  0,  0,  0,  1},
+                {  3,  1,  0,  4},
+                { -1, -1, -1, -1},
+                {  5,  2, -1,  6},
+        },{
+                { -1,  0, -1,  1},
+                {  3,  1, -1,  4},
+                { -1, -1, -1, -1},
+                {  5,  2, -1,  6},
+        }});
+
+        new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+                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[][][] {{
+                {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
+                {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
+                {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
+        }, {
+                {          -1, ids[1][0][1], ids[1][0][3]},
+                {ids[1][1][0], ids[1][1][1], ids[1][1][3]},
+                {ids[1][3][0], ids[1][3][1], ids[1][3][3]},
+        }, {
+                {ids[0][0][2], ids[0][1][2], -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
+     *                  type definitions. The first dimension represents the screens and the next
+     *                  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 {
+        long[][][] ids = new long[typeArray.length][][];
+
+        for (int i = 0; i < typeArray.length; i++) {
+            // Add screen to DB
+            long screenId = LauncherSettings.Settings.call(getMockContentResolver(),
+                    LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
+                    .getLong(LauncherSettings.Settings.EXTRA_VALUE);
+
+            ContentValues v = new ContentValues();
+            v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
+            v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
+            getMockContentResolver().insert(LauncherSettings.WorkspaceScreens.CONTENT_URI, v);
+
+            ids[i] = new long[typeArray[i].length][];
+            for (int y = 0; y < typeArray[i].length; y++) {
+                ids[i][y] = new long[typeArray[i][y].length];
+                for (int x = 0; x < typeArray[i][y].length; x++) {
+                    if (typeArray[i][y][x] < 0) {
+                        // Empty cell
+                        ids[i][y][x] = -1;
+                    } else {
+                        ids[i][y][x] = addItem(typeArray[i][y][x], screenId, DESKTOP, x, y);
+                    }
+                }
+            }
+        }
+        return ids;
+    }
+
+    /**
+     * Verifies that the workspace items are arranged in the provided order.
+     * @param ids A 3d array where the first dimension represents the screen, and the rest two
+     *            represent the workspace grid.
+     */
+    private void verifyWorkspace(long[][][] ids) {
+        ArrayList<Long> allScreens = LauncherModel.loadWorkspaceScreensDb(getMockContext());
+        assertEquals(ids.length, allScreens.size());
+        int total = 0;
+
+        for (int i = 0; i < ids.length; i++) {
+            long screenId = allScreens.get(i);
+            for (int y = 0; y < ids[i].length; y++) {
+                for (int x = 0; x < ids[i][y].length; x++) {
+                    long id = ids[i][y][x];
+
+                    Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                            new String[]{LauncherSettings.Favorites._ID},
+                            "container=-100 and screen=" + screenId +
+                                    " and cellX=" + x + " and cellY=" + y, null, null, null);
+                    if (id == -1) {
+                        assertEquals(0, c.getCount());
+                    } else {
+                        assertEquals(1, c.getCount());
+                        c.moveToNext();
+                        assertEquals(id, c.getLong(0));
+                        total++;
+                    }
+                    c.close();
+                }
+            }
+        }
+
+        // Verify that not other entry exist in the DB.
+        Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+                new String[]{LauncherSettings.Favorites._ID},
+                "container=-100", null, null, null);
+        assertEquals(total, c.getCount());
+        c.close();
+    }
+
+    /**
+     * Adds a dummy item in the DB.
+     * @param type {@link #APPLICATION} or {@link #SHORTCUT} or >= 2 for
+     *             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 = 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);
+        values.put(LauncherSettings.Favorites.CONTAINER, container);
+        values.put(LauncherSettings.Favorites.SCREEN, screen);
+        values.put(LauncherSettings.Favorites.CELLX, x);
+        values.put(LauncherSettings.Favorites.CELLY, y);
+        values.put(LauncherSettings.Favorites.SPANX, 1);
+        values.put(LauncherSettings.Favorites.SPANY, 1);
+
+        if (type == APPLICATION || type == SHORTCUT) {
+            values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
+            values.put(LauncherSettings.Favorites.INTENT, VALID_INTENT);
+        } else {
+            values.put(LauncherSettings.Favorites.ITEM_TYPE,
+                    LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
+            // Add folder items.
+            for (int i = 0; i < type; i++) {
+                addItem(APPLICATION, 0, id, 0, 0);
+            }
+        }
+
+        getMockContentResolver().insert(LauncherSettings.Favorites.CONTENT_URI, values);
+        return id;
+    }
+}
diff --git a/tests/src/com/android/launcher3/util/TestLauncherProvider.java b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
new file mode 100644
index 0000000..bd3e86c
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
@@ -0,0 +1,47 @@
+package com.android.launcher3.util;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import com.android.launcher3.LauncherProvider;
+
+/**
+ * An extension of LauncherProvider backed up by in-memory database.
+ */
+public class TestLauncherProvider extends LauncherProvider {
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    protected synchronized void createDbIfNotExists() {
+        if (mOpenHelper == null) {
+            mOpenHelper = new MyDatabaseHelper(getContext());
+        }
+    }
+
+    public SQLiteOpenHelper getHelper() {
+        createDbIfNotExists();
+        return mOpenHelper;
+    }
+
+    @Override
+    protected void notifyListeners() { }
+
+    private static class MyDatabaseHelper extends DatabaseHelper {
+        public MyDatabaseHelper(Context context) {
+            super(context, null, null);
+            initIds();
+        }
+
+        @Override
+        protected long getDefaultUserSerial() {
+            return 0;
+        }
+
+        @Override
+        protected void onEmptyDbCreated() { }
+    }
+}
\ No newline at end of file