Merge "Import translations. DO NOT MERGE" into ub-now-porkchop
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index 95b19ce..cb13291 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -997,11 +997,27 @@
         return false;
     }
 
+    private File getDefaultThumbFile() {
+        return new File(getFilesDir(), Build.VERSION.SDK_INT
+                + "_" + DEFAULT_WALLPAPER_THUMBNAIL_FILENAME);
+    }
+
+    private boolean saveDefaultWallpaperThumb(Bitmap b) {
+        // Delete old thumbnails.
+        new File(getFilesDir(), OLD_DEFAULT_WALLPAPER_THUMBNAIL_FILENAME).delete();
+        new File(getFilesDir(), DEFAULT_WALLPAPER_THUMBNAIL_FILENAME).delete();
+
+        for (int i = Build.VERSION_CODES.JELLY_BEAN; i < Build.VERSION.SDK_INT; i++) {
+            new File(getFilesDir(), i + "_" + DEFAULT_WALLPAPER_THUMBNAIL_FILENAME).delete();
+        }
+        return writeImageToFileAsJpeg(getDefaultThumbFile(), b);
+    }
+
     private ResourceWallpaperInfo getPreKKDefaultWallpaperInfo() {
         Resources sysRes = Resources.getSystem();
         int resId = sysRes.getIdentifier("default_wallpaper", "drawable", "android");
 
-        File defaultThumbFile = new File(getFilesDir(), DEFAULT_WALLPAPER_THUMBNAIL_FILENAME);
+        File defaultThumbFile = getDefaultThumbFile();
         Bitmap thumb = null;
         boolean defaultWallpaperExists = false;
         if (defaultThumbFile.exists()) {
@@ -1014,7 +1030,7 @@
             thumb = createThumbnail(
                     defaultThumbSize, this, null, null, sysRes, resId, rotation, false);
             if (thumb != null) {
-                defaultWallpaperExists = writeImageToFileAsJpeg(defaultThumbFile, thumb);
+                defaultWallpaperExists = saveDefaultWallpaperThumb(thumb);
             }
         }
         if (defaultWallpaperExists) {
@@ -1025,17 +1041,13 @@
 
     @TargetApi(Build.VERSION_CODES.KITKAT)
     private DefaultWallpaperInfo getDefaultWallpaper() {
-        File defaultThumbFile = new File(getFilesDir(), DEFAULT_WALLPAPER_THUMBNAIL_FILENAME);
+        File defaultThumbFile = getDefaultThumbFile();
         Bitmap thumb = null;
         boolean defaultWallpaperExists = false;
         if (defaultThumbFile.exists()) {
             thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath());
             defaultWallpaperExists = true;
         } else {
-            // Delete old thumbnail file, since we had a bug where the thumbnail wasn't being drawn
-            // before
-            new File(getFilesDir(), OLD_DEFAULT_WALLPAPER_THUMBNAIL_FILENAME).delete();
-
             Resources res = getResources();
             Point defaultThumbSize = getDefaultThumbnailSize(res);
             Drawable wallpaperDrawable = WallpaperManager.getInstance(this).getBuiltInDrawable(
@@ -1049,7 +1061,7 @@
                 c.setBitmap(null);
             }
             if (thumb != null) {
-                defaultWallpaperExists = writeImageToFileAsJpeg(defaultThumbFile, thumb);
+                defaultWallpaperExists = saveDefaultWallpaperThumb(thumb);
             }
         }
         if (defaultWallpaperExists) {
diff --git a/res/drawable-hdpi/focused_bg.9.png b/res/drawable-hdpi/focused_bg.9.png
deleted file mode 100644
index 2925ae8..0000000
--- a/res/drawable-hdpi/focused_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_add.png b/res/drawable-hdpi/ic_pageindicator_add.png
index 4bb8bfa..ab0e5db 100644
--- a/res/drawable-hdpi/ic_pageindicator_add.png
+++ b/res/drawable-hdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_current.png b/res/drawable-hdpi/ic_pageindicator_current.png
index bd15fb4..2e841f5 100644
--- a/res/drawable-hdpi/ic_pageindicator_current.png
+++ b/res/drawable-hdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_default.png b/res/drawable-hdpi/ic_pageindicator_default.png
index 2386cd8..07ab948 100644
--- a/res/drawable-hdpi/ic_pageindicator_default.png
+++ b/res/drawable-hdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_left.9.png b/res/drawable-hdpi/page_hover_left.9.png
new file mode 100644
index 0000000..5f7087c
--- /dev/null
+++ b/res/drawable-hdpi/page_hover_left.9.png
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_left_active.9.png b/res/drawable-hdpi/page_hover_left_active.9.png
new file mode 100644
index 0000000..8787126
--- /dev/null
+++ b/res/drawable-hdpi/page_hover_left_active.9.png
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_left_holo.9.png b/res/drawable-hdpi/page_hover_left_holo.9.png
deleted file mode 100644
index 8a1aa5f..0000000
--- a/res/drawable-hdpi/page_hover_left_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_right.9.png b/res/drawable-hdpi/page_hover_right.9.png
new file mode 100644
index 0000000..ef991b7
--- /dev/null
+++ b/res/drawable-hdpi/page_hover_right.9.png
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_right_active.9.png b/res/drawable-hdpi/page_hover_right_active.9.png
new file mode 100644
index 0000000..cd5b42d
--- /dev/null
+++ b/res/drawable-hdpi/page_hover_right_active.9.png
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_right_holo.9.png b/res/drawable-hdpi/page_hover_right_holo.9.png
deleted file mode 100644
index abf8f51..0000000
--- a/res/drawable-hdpi/page_hover_right_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/quantum_panel.9.png b/res/drawable-hdpi/quantum_panel.9.png
index a005e0b..914961a 100644
--- a/res/drawable-hdpi/quantum_panel.9.png
+++ b/res/drawable-hdpi/quantum_panel.9.png
Binary files differ
diff --git a/res/drawable-hdpi/virtual_preload.9.png b/res/drawable-hdpi/virtual_preload.9.png
new file mode 100644
index 0000000..71e5326
--- /dev/null
+++ b/res/drawable-hdpi/virtual_preload.9.png
Binary files differ
diff --git a/res/drawable-hdpi/virtual_preload_folder.9.png b/res/drawable-hdpi/virtual_preload_folder.9.png
new file mode 100644
index 0000000..ece3226
--- /dev/null
+++ b/res/drawable-hdpi/virtual_preload_folder.9.png
Binary files differ
diff --git a/res/drawable-mdpi/focused_bg.9.png b/res/drawable-mdpi/focused_bg.9.png
deleted file mode 100644
index 89c29ac..0000000
--- a/res/drawable-mdpi/focused_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_add.png b/res/drawable-mdpi/ic_pageindicator_add.png
index e20ffa7..11659a3 100644
--- a/res/drawable-mdpi/ic_pageindicator_add.png
+++ b/res/drawable-mdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_current.png b/res/drawable-mdpi/ic_pageindicator_current.png
index bb49d31..08f43b4 100644
--- a/res/drawable-mdpi/ic_pageindicator_current.png
+++ b/res/drawable-mdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_default.png b/res/drawable-mdpi/ic_pageindicator_default.png
index 50d0989..635be4a 100644
--- a/res/drawable-mdpi/ic_pageindicator_default.png
+++ b/res/drawable-mdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_left.9.png b/res/drawable-mdpi/page_hover_left.9.png
new file mode 100644
index 0000000..0d537e6
--- /dev/null
+++ b/res/drawable-mdpi/page_hover_left.9.png
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_left_active.9.png b/res/drawable-mdpi/page_hover_left_active.9.png
new file mode 100644
index 0000000..3004f9e
--- /dev/null
+++ b/res/drawable-mdpi/page_hover_left_active.9.png
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_left_holo.9.png b/res/drawable-mdpi/page_hover_left_holo.9.png
deleted file mode 100644
index 561d3cd..0000000
--- a/res/drawable-mdpi/page_hover_left_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_right.9.png b/res/drawable-mdpi/page_hover_right.9.png
new file mode 100644
index 0000000..c0fbb7d
--- /dev/null
+++ b/res/drawable-mdpi/page_hover_right.9.png
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_right_active.9.png b/res/drawable-mdpi/page_hover_right_active.9.png
new file mode 100644
index 0000000..6d39687
--- /dev/null
+++ b/res/drawable-mdpi/page_hover_right_active.9.png
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_right_holo.9.png b/res/drawable-mdpi/page_hover_right_holo.9.png
deleted file mode 100644
index 2681f23..0000000
--- a/res/drawable-mdpi/page_hover_right_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/quantum_panel.9.png b/res/drawable-mdpi/quantum_panel.9.png
index 785f573..b9b9506 100644
--- a/res/drawable-mdpi/quantum_panel.9.png
+++ b/res/drawable-mdpi/quantum_panel.9.png
Binary files differ
diff --git a/res/drawable-mdpi/virtual_preload.9.png b/res/drawable-mdpi/virtual_preload.9.png
new file mode 100644
index 0000000..a3c7519
--- /dev/null
+++ b/res/drawable-mdpi/virtual_preload.9.png
Binary files differ
diff --git a/res/drawable-mdpi/virtual_preload_folder.9.png b/res/drawable-mdpi/virtual_preload_folder.9.png
new file mode 100644
index 0000000..fa2f131
--- /dev/null
+++ b/res/drawable-mdpi/virtual_preload_folder.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/focused_bg.9.png b/res/drawable-xhdpi/focused_bg.9.png
deleted file mode 100644
index 197a269..0000000
--- a/res/drawable-xhdpi/focused_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_add.png b/res/drawable-xhdpi/ic_pageindicator_add.png
index 9659e6f..af1da2d 100644
--- a/res/drawable-xhdpi/ic_pageindicator_add.png
+++ b/res/drawable-xhdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_current.png b/res/drawable-xhdpi/ic_pageindicator_current.png
index 07b3137..0e9a52f 100644
--- a/res/drawable-xhdpi/ic_pageindicator_current.png
+++ b/res/drawable-xhdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_default.png b/res/drawable-xhdpi/ic_pageindicator_default.png
index 0351d57..d0f14cd 100644
--- a/res/drawable-xhdpi/ic_pageindicator_default.png
+++ b/res/drawable-xhdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_left.9.png b/res/drawable-xhdpi/page_hover_left.9.png
new file mode 100644
index 0000000..9b0def8
--- /dev/null
+++ b/res/drawable-xhdpi/page_hover_left.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_left_active.9.png b/res/drawable-xhdpi/page_hover_left_active.9.png
new file mode 100644
index 0000000..6440bdf
--- /dev/null
+++ b/res/drawable-xhdpi/page_hover_left_active.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_left_holo.9.png b/res/drawable-xhdpi/page_hover_left_holo.9.png
deleted file mode 100644
index 4972a2e..0000000
--- a/res/drawable-xhdpi/page_hover_left_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_right.9.png b/res/drawable-xhdpi/page_hover_right.9.png
new file mode 100644
index 0000000..c7bdfe9
--- /dev/null
+++ b/res/drawable-xhdpi/page_hover_right.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_right_active.9.png b/res/drawable-xhdpi/page_hover_right_active.9.png
new file mode 100644
index 0000000..43660e5
--- /dev/null
+++ b/res/drawable-xhdpi/page_hover_right_active.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_right_holo.9.png b/res/drawable-xhdpi/page_hover_right_holo.9.png
deleted file mode 100644
index b99461f..0000000
--- a/res/drawable-xhdpi/page_hover_right_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/quantum_panel.9.png b/res/drawable-xhdpi/quantum_panel.9.png
index 5726fd2..1bbb937 100644
--- a/res/drawable-xhdpi/quantum_panel.9.png
+++ b/res/drawable-xhdpi/quantum_panel.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/virtual_preload.9.png b/res/drawable-xhdpi/virtual_preload.9.png
new file mode 100644
index 0000000..d2c3fea
--- /dev/null
+++ b/res/drawable-xhdpi/virtual_preload.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/virtual_preload_folder.9.png b/res/drawable-xhdpi/virtual_preload_folder.9.png
new file mode 100644
index 0000000..1f9202b
--- /dev/null
+++ b/res/drawable-xhdpi/virtual_preload_folder.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/focused_bg.9.png b/res/drawable-xxhdpi/focused_bg.9.png
deleted file mode 100644
index 84d3062..0000000
--- a/res/drawable-xxhdpi/focused_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_add.png b/res/drawable-xxhdpi/ic_pageindicator_add.png
index 591b189..c288952 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_add.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_current.png b/res/drawable-xxhdpi/ic_pageindicator_current.png
index 4e4660f..b74e92e 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_current.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_default.png b/res/drawable-xxhdpi/ic_pageindicator_default.png
index 1cccb17..e362ece 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_default.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_left.9.png b/res/drawable-xxhdpi/page_hover_left.9.png
new file mode 100644
index 0000000..0c0eddd
--- /dev/null
+++ b/res/drawable-xxhdpi/page_hover_left.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_left_active.9.png b/res/drawable-xxhdpi/page_hover_left_active.9.png
new file mode 100644
index 0000000..a9ae222
--- /dev/null
+++ b/res/drawable-xxhdpi/page_hover_left_active.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_left_holo.9.png b/res/drawable-xxhdpi/page_hover_left_holo.9.png
deleted file mode 100644
index 626aafb..0000000
--- a/res/drawable-xxhdpi/page_hover_left_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_right.9.png b/res/drawable-xxhdpi/page_hover_right.9.png
new file mode 100644
index 0000000..d61f94f
--- /dev/null
+++ b/res/drawable-xxhdpi/page_hover_right.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_right_active.9.png b/res/drawable-xxhdpi/page_hover_right_active.9.png
new file mode 100644
index 0000000..079ee1e
--- /dev/null
+++ b/res/drawable-xxhdpi/page_hover_right_active.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_right_holo.9.png b/res/drawable-xxhdpi/page_hover_right_holo.9.png
deleted file mode 100644
index 66257dc..0000000
--- a/res/drawable-xxhdpi/page_hover_right_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/quantum_panel.9.png b/res/drawable-xxhdpi/quantum_panel.9.png
index 035cdc4..4392aa0 100644
--- a/res/drawable-xxhdpi/quantum_panel.9.png
+++ b/res/drawable-xxhdpi/quantum_panel.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/virtual_preload.9.png b/res/drawable-xxhdpi/virtual_preload.9.png
index 0ec1740..93e3b33 100644
--- a/res/drawable-xxhdpi/virtual_preload.9.png
+++ b/res/drawable-xxhdpi/virtual_preload.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/virtual_preload_folder.9.png b/res/drawable-xxhdpi/virtual_preload_folder.9.png
index ee80c76..fae19b3 100644
--- a/res/drawable-xxhdpi/virtual_preload_folder.9.png
+++ b/res/drawable-xxhdpi/virtual_preload_folder.9.png
Binary files differ
diff --git a/res/drawable/focusable_view_bg.xml b/res/drawable/focusable_view_bg.xml
index 66661e2..e156513 100644
--- a/res/drawable/focusable_view_bg.xml
+++ b/res/drawable/focusable_view_bg.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
+<!--
+     Copyright (C) 2011 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -15,5 +16,11 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true" android:drawable="@drawable/focused_bg" />
-</selector>
+
+    <item android:state_focused="true">
+        <shape android:shape="rectangle">
+            <solid android:color="@color/focused_background" />
+        </shape>
+    </item>
+
+</selector>
\ No newline at end of file
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 7791609..0af9e59 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -30,6 +30,11 @@
         android:layout_height="match_parent"
         android:fitsSystemWindows="true">
 
+        <com.android.launcher3.FocusIndicatorView
+            android:id="@+id/focus_indicator"
+            android:layout_width="52dp"
+            android:layout_height="52dp" />
+
         <!-- The workspace contains 5 screens of cells -->
         <com.android.launcher3.Workspace
             android:id="@+id/workspace"
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 574b73e..46e0c0f 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -29,6 +29,11 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
+        <com.android.launcher3.FocusIndicatorView
+            android:id="@+id/focus_indicator"
+            android:layout_width="52dp"
+            android:layout_height="52dp" />
+
         <!-- The workspace contains 5 screens of cells -->
         <com.android.launcher3.Workspace
             android:id="@+id/workspace"
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 685d03c..f8b8437 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -30,6 +30,11 @@
         android:layout_height="match_parent"
         android:fitsSystemWindows="true">
 
+        <com.android.launcher3.FocusIndicatorView
+            android:id="@+id/focus_indicator"
+            android:layout_width="52dp"
+            android:layout_height="52dp" />
+
         <!-- The workspace contains 5 screens of cells -->
         <com.android.launcher3.Workspace
             android:id="@+id/workspace"
diff --git a/res/layout/all_apps_button.xml b/res/layout/all_apps_button.xml
index 1b9ea08..9d6d82b 100644
--- a/res/layout/all_apps_button.xml
+++ b/res/layout/all_apps_button.xml
@@ -16,5 +16,4 @@
 
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/WorkspaceIcon"
-   android:focusable="true"
-   android:background="@drawable/focusable_view_bg" />
+   android:focusable="true" />
diff --git a/res/layout/application.xml b/res/layout/application.xml
index e4909dd..c21dea0 100644
--- a/res/layout/application.xml
+++ b/res/layout/application.xml
@@ -16,5 +16,4 @@
 
 <com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/WorkspaceIcon"
-   android:focusable="true"
-   android:background="@drawable/focusable_view_bg" />
+   android:focusable="true" />
diff --git a/res/layout/apps_customize_application.xml b/res/layout/apps_customize_application.xml
index b48b9b7..c56cdf3 100644
--- a/res/layout/apps_customize_application.xml
+++ b/res/layout/apps_customize_application.xml
@@ -14,11 +14,8 @@
      limitations under the License.
 -->
 
-<com.android.launcher3.PagedViewIcon
+<com.android.launcher3.BubbleTextView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
-
     style="@style/WorkspaceIcon.AppsCustomize"
     android:id="@+id/application_icon"
-    android:focusable="true"
-    android:background="@drawable/focusable_view_bg" />
+    android:focusable="true" />
diff --git a/res/layout/folder_application.xml b/res/layout/folder_application.xml
index 37dd79d..b48b613 100644
--- a/res/layout/folder_application.xml
+++ b/res/layout/folder_application.xml
@@ -16,5 +16,4 @@
 
 <com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/WorkspaceIcon.Folder"
-   android:focusable="true"
-   android:background="@drawable/focusable_view_bg" />
+   android:focusable="true" />
diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml
index 5147f99..fd45d76 100644
--- a/res/layout/folder_icon.xml
+++ b/res/layout/folder_icon.xml
@@ -19,8 +19,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
-    android:focusable="true"
-    android:background="@drawable/focusable_view_bg">
+    android:focusable="true" >
     <ImageView
         android:id="@+id/preview_background"
         android:layout_gravity="center_horizontal"
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 552e84c..0db60c9 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -106,7 +106,6 @@
     <declare-styleable name="BubbleTextView">
         <!-- A spacing override for the icons within a page -->
         <attr name="customShadows" format="boolean" />
-        <attr name="glowColor" format="color" />
     </declare-styleable>
 
     <!-- AppsCustomizePagedView specific attributes.  These attributes are used to
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 4e64d41..8aa2184 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -24,6 +24,7 @@
     <color name="info_target_hover_tint">#DA0099CC</color>
 
     <color name="bubble_dark_background">#20000000</color>
+    <color name="focused_background">#80c6c5c5</color>
 
     <color name="appwidget_error_color">#FCCC</color>
 
@@ -33,7 +34,6 @@
 
     <color name="quantum_panel_text_color">#FF666666</color>
     <color name="quantum_panel_text_shadow_color">#FFC4C4C4</color>
-    <color name="folder_items_glow_color">#FFCCCCCC</color>
     <color name="outline_color">#FFFFFFFF</color>
     <color name="widget_text_panel">#FF374248</color>
 
diff --git a/res/values/config.xml b/res/values/config.xml
index 0da0b70..19ae44c 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -79,7 +79,9 @@
     <integer name="config_dropAnimMaxDuration">500</integer>
 
     <!-- The duration of the UserFolder opening and closing animation -->
-    <integer name="config_folderAnimDuration">120</integer>
+    <integer name="config_folderExpandDuration">120</integer>
+    <integer name="config_materialFolderExpandDuration">200</integer>
+    <integer name="config_materialFolderExpandStagger">60</integer>
 
     <!-- The distance at which the animation should take the max duration -->
     <integer name="config_dropAnimMaxDist">800</integer>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 893d7c0..b2e183c 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -101,4 +101,8 @@
     <!-- The amount that the preview contents are inset from the preview background -->
     <dimen name="folder_preview_padding">4dp</dimen>
     <dimen name="folder_name_padding">10dp</dimen>
+
+<!-- Sizes for managed profile badges -->
+    <dimen name="profile_badge_size">24dp</dimen>
+    <dimen name="profile_badge_margin">4dp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index aa5f7b6..ff3509b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -243,6 +243,12 @@
     <string name="workspace_cling_title">Organize your space</string>
     <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
     <string name="workspace_cling_move_item">Touch &amp; hold background to manage wallpaper, widgets and settings.</string>
+    <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+    <string name="workspace_cling_longpress_title">Wallpapers, widgets, &amp; settings</string>
+    <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+    <string name="workspace_cling_longpress_description">Touch &amp; hold background to customize</string>
+    <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+    <string name="workspace_cling_longpress_dismiss">GOT IT</string>
     <!-- The title text for the Folder cling [CHAR_LIMIT=30] -->
     <string name="folder_cling_title">Here\'s a folder</string>
     <!-- The description of how to create a folder [CHAR_LIMIT=70] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 6079eee..0569306 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -91,7 +91,8 @@
         <item name="android:shadowRadius">2.0</item>
         <item name="android:shadowDx">0</item>
         <item name="android:shadowDy">2</item>
-        <item name="android:shadowColor">#FFC4C4C4</item>
+        <item name="android:shadowColor">@color/quantum_panel_text_shadow_color</item>
+        <item name="customShadows">false</item>
     </style>
 
     <style name="WorkspaceIcon.Folder">
@@ -103,7 +104,6 @@
         <item name="android:shadowDy">2</item>
 
         <item name="customShadows">false</item>
-        <item name="glowColor">@color/folder_items_glow_color</item>
     </style>
 
     <style name="SearchDropTargetBar">
diff --git a/src/com/android/launcher3/AppsCustomizeCellLayout.java b/src/com/android/launcher3/AppsCustomizeCellLayout.java
index 3c8bda9..76b81d2 100644
--- a/src/com/android/launcher3/AppsCustomizeCellLayout.java
+++ b/src/com/android/launcher3/AppsCustomizeCellLayout.java
@@ -20,8 +20,16 @@
 import android.view.View;
 
 public class AppsCustomizeCellLayout extends CellLayout implements Page {
+
+    final FocusIndicatorView mFocusHandlerView;
+
     public AppsCustomizeCellLayout(Context context) {
         super(context);
+
+        mFocusHandlerView = new FocusIndicatorView(context);
+        addView(mFocusHandlerView, 0);
+        mFocusHandlerView.getLayoutParams().width = FocusIndicatorView.DEFAULT_LAYOUT_SIZE;
+        mFocusHandlerView.getLayoutParams().height = FocusIndicatorView.DEFAULT_LAYOUT_SIZE;
     }
 
     @Override
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index d23e65f..9f9c34b 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -28,7 +28,6 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -44,12 +43,12 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
 import android.widget.GridLayout;
 import android.widget.ImageView;
 import android.widget.Toast;
 
 import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -144,8 +143,7 @@
  */
 public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
         View.OnClickListener, View.OnKeyListener, DragSource,
-        PagedViewIcon.PressedCallback, PagedViewWidget.ShortPressListener,
-        LauncherTransitionable {
+        PagedViewWidget.ShortPressListener, LauncherTransitionable {
     static final String TAG = "AppsCustomizePagedView";
 
     private static Rect sTmpRect = new Rect();
@@ -167,7 +165,6 @@
 
     // Save and Restore
     private int mSaveInstanceStateItemIndex = -1;
-    private PagedViewIcon mPressedIcon;
 
     // Content
     private ArrayList<AppInfo> mApps;
@@ -179,7 +176,6 @@
     private int mClingFocusedY;
 
     // Caching
-    private Canvas mCanvas;
     private IconCache mIconCache;
 
     // Dimens
@@ -190,16 +186,6 @@
     private int mNumWidgetPages;
     private Rect mAllAppsPadding = new Rect();
 
-    // Relating to the scroll and overscroll effects
-    Workspace.ZInterpolator mZInterpolator = new Workspace.ZInterpolator(0.5f);
-    private static float CAMERA_DISTANCE = 6500;
-    private static float TRANSITION_SCALE_FACTOR = 0.74f;
-    private static float TRANSITION_PIVOT = 0.65f;
-    private static float TRANSITION_MAX_ROTATION = 22;
-    private static final boolean PERFORM_OVERSCROLL_ROTATION = false;
-    private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f);
-    private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4);
-
     // Previews & outlines
     ArrayList<AppsCustomizeAsyncTask> mRunningTasks;
     private static final int sPageSleepDelay = 200;
@@ -237,13 +223,10 @@
         mApps = new ArrayList<AppInfo>();
         mWidgets = new ArrayList<Object>();
         mIconCache = (LauncherAppState.getInstance()).getIconCache();
-        mCanvas = new Canvas();
         mRunningTasks = new ArrayList<AppsCustomizeAsyncTask>();
 
         // Save the default widget preview background
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0);
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
         mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2);
         mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2);
         mClingFocusedX = a.getInt(R.styleable.AppsCustomizePagedView_clingFocusedX, 0);
@@ -404,7 +387,6 @@
                 if (!app.shouldShowAppOrWidgetProvider(widget.provider)) {
                     continue;
                 }
-                widget.label = widget.label.trim();
                 if (widget.minWidth > 0 && widget.minHeight > 0) {
                     // Ensure that all widgets we show can be added on a workspace of this size
                     int[] spanXY = Launcher.getSpanForWidget(mLauncher, widget);
@@ -454,39 +436,29 @@
     @Override
     public void onClick(View v) {
         // When we have exited all apps or are in transition, disregard clicks
-        if (!mLauncher.isAllAppsVisible() ||
-                mLauncher.getWorkspace().isSwitchingState()) return;
+        if (!mLauncher.isAllAppsVisible()
+                || mLauncher.getWorkspace().isSwitchingState()
+                || !(v instanceof PagedViewWidget)) return;
 
-        if (v instanceof PagedViewIcon) {
-            // Animate some feedback to the click
-            final AppInfo appInfo = (AppInfo) v.getTag();
-
-            // Lock the drawable state to pressed until we return to Launcher
-            if (mPressedIcon != null) {
-                mPressedIcon.lockDrawableState();
-            }
-            mLauncher.onClickPagedViewIcon(v, appInfo);
-        } else if (v instanceof PagedViewWidget) {
-            // Let the user know that they have to long press to add a widget
-            if (mWidgetInstructionToast != null) {
-                mWidgetInstructionToast.cancel();
-            }
-            mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add,
-                Toast.LENGTH_SHORT);
-            mWidgetInstructionToast.show();
-
-            // Create a little animation to show that the widget can move
-            float offsetY = getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
-            final ImageView p = (ImageView) v.findViewById(R.id.widget_preview);
-            AnimatorSet bounce = LauncherAnimUtils.createAnimatorSet();
-            ValueAnimator tyuAnim = LauncherAnimUtils.ofFloat(p, "translationY", offsetY);
-            tyuAnim.setDuration(125);
-            ValueAnimator tydAnim = LauncherAnimUtils.ofFloat(p, "translationY", 0f);
-            tydAnim.setDuration(100);
-            bounce.play(tyuAnim).before(tydAnim);
-            bounce.setInterpolator(new AccelerateInterpolator());
-            bounce.start();
+        // Let the user know that they have to long press to add a widget
+        if (mWidgetInstructionToast != null) {
+            mWidgetInstructionToast.cancel();
         }
+        mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add,
+            Toast.LENGTH_SHORT);
+        mWidgetInstructionToast.show();
+
+        // Create a little animation to show that the widget can move
+        float offsetY = getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
+        final ImageView p = (ImageView) v.findViewById(R.id.widget_preview);
+        AnimatorSet bounce = LauncherAnimUtils.createAnimatorSet();
+        ValueAnimator tyuAnim = LauncherAnimUtils.ofFloat(p, "translationY", offsetY);
+        tyuAnim.setDuration(125);
+        ValueAnimator tydAnim = LauncherAnimUtils.ofFloat(p, "translationY", 0f);
+        tydAnim.setDuration(100);
+        bounce.play(tyuAnim).before(tydAnim);
+        bounce.setInterpolator(new AccelerateInterpolator());
+        bounce.start();
     }
 
     public boolean onKey(View v, int keyCode, KeyEvent event) {
@@ -502,7 +474,6 @@
     }
 
     private void beginDraggingApplication(View v) {
-        mLauncher.getWorkspace().onDragStartedWithItem(v);
         mLauncher.getWorkspace().beginDragShared(v, this);
     }
 
@@ -544,18 +515,9 @@
             @Override
             public void run() {
                 mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
-                // Options will be null for platforms with JB or lower, so this serves as an
-                // SDK level check.
-                if (options == null) {
-                    if (AppWidgetManager.getInstance(mLauncher).bindAppWidgetIdIfAllowed(
-                            mWidgetLoadingId, info.componentName)) {
-                        mWidgetCleanupState = WIDGET_BOUND;
-                    }
-                } else {
-                    if (AppWidgetManager.getInstance(mLauncher).bindAppWidgetIdIfAllowed(
-                            mWidgetLoadingId, info.componentName, options)) {
-                        mWidgetCleanupState = WIDGET_BOUND;
-                    }
+                if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed(
+                        mWidgetLoadingId, pInfo, options)) {
+                    mWidgetCleanupState = WIDGET_BOUND;
                 }
             }
         };
@@ -683,9 +645,8 @@
 
             int[] previewSizeBeforeScale = new int[1];
 
-            preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.componentName,
-                    createWidgetInfo.previewImage, createWidgetInfo.icon, spanX, spanY,
-                    maxWidth, maxHeight, null, previewSizeBeforeScale);
+            preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info,
+                    spanX, spanY, maxWidth, maxHeight, null, previewSizeBeforeScale);
 
             // Compare the size of the drag preview to the preview in the AppsCustomize tray
             int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0],
@@ -702,15 +663,7 @@
         } else {
             PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag();
             Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.shortcutActivityInfo);
-            preview = Bitmap.createBitmap(icon.getIntrinsicWidth(),
-                    icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
-
-            mCanvas.setBitmap(preview);
-            mCanvas.save();
-            WidgetPreviewLoader.renderDrawableToBitmap(icon, preview, 0, 0,
-                    icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
-            mCanvas.restore();
-            mCanvas.setBitmap(null);
+            preview = Utilities.createIconBitmap(icon, mLauncher);
             createItemInfo.spanX = createItemInfo.spanY = 1;
         }
 
@@ -736,7 +689,7 @@
     protected boolean beginDragging(final View v) {
         if (!super.beginDragging(v)) return false;
 
-        if (v instanceof PagedViewIcon) {
+        if (v instanceof BubbleTextView) {
             beginDraggingApplication(v);
         } else if (v instanceof PagedViewWidget) {
             if (!beginDraggingWidget(v)) {
@@ -751,9 +704,6 @@
             public void run() {
                 // We don't enter spring-loaded mode if the drag has been cancelled
                 if (mLauncher.getDragController().isDragging()) {
-                    // Reset the alpha on the dragged icon before we drag
-                    resetDrawableState();
-
                     // Go into spring loaded mode (must happen before we startDrag())
                     mLauncher.enterSpringLoadedDragMode();
                 }
@@ -1002,13 +952,14 @@
         ArrayList<Bitmap> images = new ArrayList<Bitmap>();
         for (int i = startIndex; i < endIndex; ++i) {
             AppInfo info = mApps.get(i);
-            PagedViewIcon icon = (PagedViewIcon) mLayoutInflater.inflate(
+            BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
                     R.layout.apps_customize_application, layout, false);
-            icon.applyFromApplicationInfo(info, true, this);
-            icon.setOnClickListener(this);
+            icon.applyFromApplicationInfo(info);
+            icon.setOnClickListener(mLauncher);
             icon.setOnLongClickListener(this);
             icon.setOnTouchListener(this);
             icon.setOnKeyListener(this);
+            icon.setOnFocusChangeListener(layout.mFocusHandlerView);
 
             int index = i - startIndex;
             int x = index % mCellCountX;
@@ -1365,73 +1316,7 @@
     // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack.
     @Override
     protected void screenScrolled(int screenCenter) {
-        final boolean isRtl = isLayoutRtl();
         super.screenScrolled(screenCenter);
-
-        for (int i = 0; i < getChildCount(); i++) {
-            View v = getPageAt(i);
-            if (v != null) {
-                float scrollProgress = getScrollProgress(screenCenter, v, i);
-
-                float interpolatedProgress;
-                float translationX;
-                float maxScrollProgress = Math.max(0, scrollProgress);
-                float minScrollProgress = Math.min(0, scrollProgress);
-
-                if (isRtl) {
-                    translationX = maxScrollProgress * v.getMeasuredWidth();
-                    interpolatedProgress = mZInterpolator.getInterpolation(Math.abs(maxScrollProgress));
-                } else {
-                    translationX = minScrollProgress * v.getMeasuredWidth();
-                    interpolatedProgress = mZInterpolator.getInterpolation(Math.abs(minScrollProgress));
-                }
-                float scale = (1 - interpolatedProgress) +
-                        interpolatedProgress * TRANSITION_SCALE_FACTOR;
-
-                float alpha;
-                if (isRtl && (scrollProgress > 0)) {
-                    alpha = mAlphaInterpolator.getInterpolation(1 - Math.abs(maxScrollProgress));
-                } else if (!isRtl && (scrollProgress < 0)) {
-                    alpha = mAlphaInterpolator.getInterpolation(1 - Math.abs(scrollProgress));
-                } else {
-                    //  On large screens we need to fade the page as it nears its leftmost position
-                    alpha = mLeftScreenAlphaInterpolator.getInterpolation(1 - scrollProgress);
-                }
-
-                v.setCameraDistance(mDensity * CAMERA_DISTANCE);
-                int pageWidth = v.getMeasuredWidth();
-                int pageHeight = v.getMeasuredHeight();
-
-                if (PERFORM_OVERSCROLL_ROTATION) {
-                    float xPivot = isRtl ? 1f - TRANSITION_PIVOT : TRANSITION_PIVOT;
-                    boolean isOverscrollingFirstPage = isRtl ? scrollProgress > 0 : scrollProgress < 0;
-                    boolean isOverscrollingLastPage = isRtl ? scrollProgress < 0 : scrollProgress > 0;
-
-                    if (i == 0 && isOverscrollingFirstPage) {
-                        // Overscroll to the left
-                        v.setPivotX(xPivot * pageWidth);
-                        v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
-                        scale = 1.0f;
-                        alpha = 1.0f;
-                        // On the first page, we don't want the page to have any lateral motion
-                        translationX = 0;
-                    } else if (i == getChildCount() - 1 && isOverscrollingLastPage) {
-                        // Overscroll to the right
-                        v.setPivotX((1 - xPivot) * pageWidth);
-                        v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
-                        scale = 1.0f;
-                        alpha = 1.0f;
-                        // On the last page, we don't want the page to have any lateral motion.
-                        translationX = 0;
-                    } else {
-                        v.setPivotY(pageHeight / 2.0f);
-                        v.setPivotX(pageWidth / 2.0f);
-                        v.setRotationY(0f);
-                    }
-                }
-            }
-        }
-
         enableHwLayersOnVisiblePages();
     }
 
@@ -1476,7 +1361,7 @@
     }
 
     protected void overScroll(float amount) {
-        acceleratedOverScroll(amount);
+        dampedOverScroll(amount);
     }
 
     /**
@@ -1634,23 +1519,6 @@
         cancelAllTasks();
     }
 
-    @Override
-    public void iconPressed(PagedViewIcon icon) {
-        // Reset the previously pressed icon and store a reference to the pressed icon so that
-        // we can reset it on return to Launcher (in Launcher.onResume())
-        if (mPressedIcon != null) {
-            mPressedIcon.resetDrawableState();
-        }
-        mPressedIcon = icon;
-    }
-
-    public void resetDrawableState() {
-        if (mPressedIcon != null) {
-            mPressedIcon.resetDrawableState();
-            mPressedIcon = null;
-        }
-    }
-
     /*
      * We load an extra page on each side to prevent flashes from scrolling and loading of the
      * widget previews in the background with the AsyncTasks.
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 5c2bb99..869b0ac 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -23,14 +23,13 @@
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.graphics.Rect;
 import android.graphics.Region;
-import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.TypedValue;
+import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 import android.widget.TextView;
@@ -44,12 +43,11 @@
 
     private static SparseArray<Theme> sPreloaderThemes = new SparseArray<>(2);
 
-    static final float SHADOW_LARGE_RADIUS = 4.0f;
-    static final float SHADOW_SMALL_RADIUS = 1.75f;
-    static final float SHADOW_Y_OFFSET = 2.0f;
-    static final int SHADOW_LARGE_COLOUR = 0xDD000000;
-    static final int SHADOW_SMALL_COLOUR = 0xCC000000;
-    static final float PADDING_H = 8.0f;
+    private static final float SHADOW_LARGE_RADIUS = 4.0f;
+    private static final float SHADOW_SMALL_RADIUS = 1.75f;
+    private static final float SHADOW_Y_OFFSET = 2.0f;
+    private static final int SHADOW_LARGE_COLOUR = 0xDD000000;
+    private static final int SHADOW_SMALL_COLOUR = 0xCC000000;
     static final float PADDING_V = 3.0f;
 
     private static final String TAG = "BubbleTextView";
@@ -57,14 +55,7 @@
     private static final boolean DEBUG = false;
 
     private HolographicOutlineHelper mOutlineHelper;
-    private final Canvas mTempCanvas = new Canvas();
-    private final Rect mTempRect = new Rect();
-    private boolean mDidInvalidateForPressedState;
-    private Bitmap mPressedOrFocusedBackground;
-    private int mFocusedOutlineColor;
-    private int mFocusedGlowColor;
-    private int mPressedOutlineColor;
-    private int mPressedGlowColor;
+    private Bitmap mPressedBackground;
 
     private float mSlop;
 
@@ -72,14 +63,15 @@
     private final boolean mCustomShadowsEnabled;
     private boolean mIsTextVisible;
 
+    // TODO: Remove custom background handling code, as no instance of BubbleTextView use any
+    // background.
     private boolean mBackgroundSizeChanged;
     private final Drawable mBackground;
 
     private boolean mStayPressed;
+    private boolean mIgnorePressedStateChange;
     private CheckLongPressHelper mLongPressHelper;
 
-    private CharSequence mDefaultText = "";
-
     public BubbleTextView(Context context) {
         this(context, null, 0);
     }
@@ -91,11 +83,8 @@
     public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
 
-        Resources res = context.getResources();
         TypedArray a = context.obtainStyledAttributes(attrs,
                 R.styleable.BubbleTextView, defStyle, 0);
-        setGlowColor(a.getColor(R.styleable.BubbleTextView_glowColor,
-                res.getColor(R.color.outline_color)));
         mCustomShadowsEnabled = a.getBoolean(R.styleable.BubbleTextView_customShadows, true);
         a.recycle();
 
@@ -143,6 +132,7 @@
         if (info.contentDescription != null) {
             setContentDescription(info.contentDescription);
         }
+        setText(info.title);
         setTag(info);
 
         if (info.wasPromise) {
@@ -150,6 +140,22 @@
         }
     }
 
+    public void applyFromApplicationInfo(AppInfo info) {
+        LauncherAppState app = LauncherAppState.getInstance();
+        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+
+        Drawable topDrawable = Utilities.createIconDrawable(info.iconBitmap);
+        topDrawable.setBounds(0, 0, grid.allAppsIconSizePx, grid.allAppsIconSizePx);
+        setCompoundDrawables(null, topDrawable, null, null);
+        setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
+        setText(info.title);
+        if (info.contentDescription != null) {
+            setContentDescription(info.contentDescription);
+        }
+        setTag(info);
+    }
+
+
     @Override
     protected boolean setFrame(int left, int top, int right, int bottom) {
         if (getLeft() != left || getRight() != right || getTop() != top || getBottom() != bottom) {
@@ -169,98 +175,22 @@
             LauncherModel.checkItemInfo((ItemInfo) tag);
         }
         super.setTag(tag);
-        if (tag instanceof ShortcutInfo) {
-            final ShortcutInfo info = (ShortcutInfo) tag;
-            mDefaultText = info.title;
-            setText(mDefaultText);
-        }
     }
 
     @Override
-    protected void drawableStateChanged() {
-        if (isPressed()) {
-            // In this case, we have already created the pressed outline on ACTION_DOWN,
-            // so we just need to do an invalidate to trigger draw
-            if (!mDidInvalidateForPressedState) {
-                setCellLayoutPressedOrFocusedIcon();
-            }
-        } else {
-            // Otherwise, either clear the pressed/focused background, or create a background
-            // for the focused state
-            final boolean backgroundEmptyBefore = mPressedOrFocusedBackground == null;
-            if (!mStayPressed) {
-                mPressedOrFocusedBackground = null;
-            }
-            if (isFocused()) {
-                if (getLayout() == null) {
-                    // In some cases, we get focus before we have been layed out. Set the
-                    // background to null so that it will get created when the view is drawn.
-                    mPressedOrFocusedBackground = null;
-                } else {
-                    mPressedOrFocusedBackground = createGlowingOutline(
-                            mTempCanvas, mFocusedGlowColor, mFocusedOutlineColor);
-                }
-                mStayPressed = false;
-                setCellLayoutPressedOrFocusedIcon();
-            }
-            final boolean backgroundEmptyNow = mPressedOrFocusedBackground == null;
-            if (!backgroundEmptyBefore && backgroundEmptyNow) {
-                setCellLayoutPressedOrFocusedIcon();
-            }
+    public void setPressed(boolean pressed) {
+        super.setPressed(pressed);
+
+        if (!mIgnorePressedStateChange) {
+            updateIconState();
         }
+    }
 
-        Drawable d = mBackground;
-        if (d != null && d.isStateful()) {
-            d.setState(getDrawableState());
+    private void updateIconState() {
+        Drawable top = getCompoundDrawables()[1];
+        if (top instanceof FastBitmapDrawable) {
+            ((FastBitmapDrawable) top).setPressed(isPressed() || mStayPressed);
         }
-        super.drawableStateChanged();
-    }
-
-    /**
-     * Draw this BubbleTextView into the given Canvas.
-     *
-     * @param destCanvas the canvas to draw on
-     * @param padding the horizontal and vertical padding to use when drawing
-     */
-    private void drawWithPadding(Canvas destCanvas, int padding) {
-        final Rect clipRect = mTempRect;
-        getDrawingRect(clipRect);
-
-        // adjust the clip rect so that we don't include the text label
-        clipRect.bottom =
-            getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V + getLayout().getLineTop(0);
-
-        // Draw the View into the bitmap.
-        // The translate of scrollX and scrollY is necessary when drawing TextViews, because
-        // they set scrollX and scrollY to large values to achieve centered text
-        destCanvas.save();
-        destCanvas.scale(getScaleX(), getScaleY(),
-                (getWidth() + padding) / 2, (getHeight() + padding) / 2);
-        destCanvas.translate(-getScrollX() + padding / 2, -getScrollY() + padding / 2);
-        destCanvas.clipRect(clipRect, Op.REPLACE);
-        draw(destCanvas);
-        destCanvas.restore();
-    }
-
-    public void setGlowColor(int color) {
-        mFocusedOutlineColor = mFocusedGlowColor = mPressedOutlineColor = mPressedGlowColor = color;
-    }
-
-    /**
-     * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
-     * Responsibility for the bitmap is transferred to the caller.
-     */
-    private Bitmap createGlowingOutline(Canvas canvas, int outlineColor, int glowColor) {
-        final int padding = mOutlineHelper.mMaxOuterBlurRadius;
-        final Bitmap b = Bitmap.createBitmap(
-                getWidth() + padding, getHeight() + padding, Bitmap.Config.ARGB_8888);
-
-        canvas.setBitmap(b);
-        drawWithPadding(canvas, padding);
-        mOutlineHelper.applyExtraThickExpensiveOutlineWithBlur(b, canvas, glowColor, outlineColor);
-        canvas.setBitmap(null);
-
-        return b;
     }
 
     @Override
@@ -271,20 +201,11 @@
 
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
-                // So that the pressed outline is visible immediately when isPressed() is true,
+                // So that the pressed outline is visible immediately on setStayPressed(),
                 // we pre-create it on ACTION_DOWN (it takes a small but perceptible amount of time
                 // to create it)
-                if (mPressedOrFocusedBackground == null) {
-                    mPressedOrFocusedBackground = createGlowingOutline(
-                            mTempCanvas, mPressedGlowColor, mPressedOutlineColor);
-                }
-                // Invalidate so the pressed state is visible, or set a flag so we know that we
-                // have to call invalidate as soon as the state is "pressed"
-                if (isPressed()) {
-                    mDidInvalidateForPressedState = true;
-                    setCellLayoutPressedOrFocusedIcon();
-                } else {
-                    mDidInvalidateForPressedState = false;
+                if (mPressedBackground == null) {
+                    mPressedBackground = mOutlineHelper.createMediumDropShadow(this);
                 }
 
                 mLongPressHelper.postCheckForLongPress();
@@ -294,7 +215,7 @@
                 // If we've touched down and up on an item, and it's still not "pressed", then
                 // destroy the pressed outline
                 if (!isPressed()) {
-                    mPressedOrFocusedBackground = null;
+                    mPressedBackground = null;
                 }
 
                 mLongPressHelper.cancelLongPress();
@@ -311,34 +232,47 @@
     void setStayPressed(boolean stayPressed) {
         mStayPressed = stayPressed;
         if (!stayPressed) {
-            mPressedOrFocusedBackground = null;
+            mPressedBackground = null;
         }
-        setCellLayoutPressedOrFocusedIcon();
+
+        // Only show the shadow effect when persistent pressed state is set.
+        if (getParent() instanceof ShortcutAndWidgetContainer) {
+            CellLayout layout = (CellLayout) getParent().getParent();
+            layout.setPressedIcon(this, mPressedBackground, mOutlineHelper.shadowBitmapPadding);
+        }
+
+        updateIconState();
     }
 
-    void setCellLayoutPressedOrFocusedIcon() {
-        // Disable pressed state when the icon is in preloader state.
-        if ((getParent() instanceof ShortcutAndWidgetContainer) &&
-                !(getCompoundDrawables()[1] instanceof PreloadIconDrawable)){
-            ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) getParent();
-            if (parent != null) {
-                CellLayout layout = (CellLayout) parent.getParent();
-                layout.setPressedOrFocusedIcon((mPressedOrFocusedBackground != null) ? this : null);
+    void clearPressedBackground() {
+        setPressed(false);
+        setStayPressed(false);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (super.onKeyDown(keyCode, event)) {
+            // Pre-create shadow so show immediately on click.
+            if (mPressedBackground == null) {
+                mPressedBackground = mOutlineHelper.createMediumDropShadow(this);
             }
+            return true;
         }
+        return false;
     }
 
-    void clearPressedOrFocusedBackground() {
-        mPressedOrFocusedBackground = null;
-        setCellLayoutPressedOrFocusedIcon();
-    }
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        // Unlike touch events, keypress event propagate pressed state change immediately,
+        // without waiting for onClickHandler to execute. Disable pressed state changes here
+        // to avoid flickering.
+        mIgnorePressedStateChange = true;
+        boolean result = super.onKeyUp(keyCode, event);
 
-    Bitmap getPressedOrFocusedBackground() {
-        return mPressedOrFocusedBackground;
-    }
-
-    int getPressedOrFocusedBackgroundPadding() {
-        return mOutlineHelper.mMaxOuterBlurRadius / 2;
+        mPressedBackground = null;
+        mIgnorePressedStateChange = false;
+        updateIconState();
+        return result;
     }
 
     @Override
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 1073764..d0b3f43 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -71,8 +71,8 @@
     private int mWidthGap;
     private int mHeightGap;
     private int mMaxGap;
-    private boolean mScrollingTransformsDirty = false;
     private boolean mDropPending = false;
+    private boolean mIsDragTarget = true;
 
     // 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.
@@ -123,7 +123,7 @@
     private int mDragOutlineCurrent = 0;
     private final Paint mDragOutlinePaint = new Paint();
 
-    private BubbleTextView mPressedOrFocusedIcon;
+    private final FastBitmapView mTouchFeedbackView;
 
     private HashMap<CellLayout.LayoutParams, Animator> mReorderAnimators = new
             HashMap<CellLayout.LayoutParams, Animator>();
@@ -288,6 +288,9 @@
         mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
                 mCountX, mCountY);
 
+        mTouchFeedbackView = new FastBitmapView(context);
+        // Make the feedback view large enough to hold the blur bitmap.
+        addView(mTouchFeedbackView, (int) (grid.cellWidthPx * 1.5), (int) (grid.cellHeightPx * 1.5));
         addView(mShortcutsAndWidgets);
     }
 
@@ -334,14 +337,6 @@
         return mDropPending;
     }
 
-    private void invalidateBubbleTextView(BubbleTextView icon) {
-        final int padding = icon.getPressedOrFocusedBackgroundPadding();
-        invalidate(icon.getLeft() + getPaddingLeft() - padding,
-                icon.getTop() + getPaddingTop() - padding,
-                icon.getRight() + getPaddingLeft() + padding,
-                icon.getBottom() + getPaddingTop() + padding);
-    }
-
     void setOverScrollAmount(float r, boolean left) {
         if (left && mOverScrollForegroundDrawable != mOverScrollLeft) {
             mOverScrollForegroundDrawable = mOverScrollLeft;
@@ -355,24 +350,23 @@
         invalidate();
     }
 
-    void setPressedOrFocusedIcon(BubbleTextView icon) {
-        // We draw the pressed or focused BubbleTextView's background in CellLayout because it
-        // requires an expanded clip rect (due to the glow's blur radius)
-        BubbleTextView oldIcon = mPressedOrFocusedIcon;
-        mPressedOrFocusedIcon = icon;
-        if (oldIcon != null) {
-            invalidateBubbleTextView(oldIcon);
-        }
-        if (mPressedOrFocusedIcon != null) {
-            invalidateBubbleTextView(mPressedOrFocusedIcon);
-        }
-    }
-
-    void setIsDragOverlapping(boolean isDragOverlapping) {
-        if (mIsDragOverlapping != isDragOverlapping) {
-            mIsDragOverlapping = isDragOverlapping;
-            setUseActiveGlowBackground(mIsDragOverlapping);
-            invalidate();
+    void setPressedIcon(BubbleTextView icon, Bitmap background, int padding) {
+        if (icon == null || background == null) {
+            mTouchFeedbackView.setBitmap(null);
+            mTouchFeedbackView.animate().cancel();
+        } else {
+            int offset = getMeasuredWidth() - getPaddingLeft() - getPaddingRight()
+                    - (mCountX * mCellWidth);
+            mTouchFeedbackView.setTranslationX(icon.getLeft() + (int) Math.ceil(offset / 2f)
+                    - padding);
+            mTouchFeedbackView.setTranslationY(icon.getTop() - padding);
+            if (mTouchFeedbackView.setBitmap(background)) {
+                mTouchFeedbackView.setAlpha(0);
+                mTouchFeedbackView.animate().alpha(1)
+                    .setDuration(FastBitmapDrawable.CLICK_FEEDBACK_DURATION)
+                    .setInterpolator(FastBitmapDrawable.CLICK_FEEDBACK_INTERPOLATOR)
+                    .start();
+            }
         }
     }
 
@@ -384,27 +378,26 @@
         mDrawBackground = false;
     }
 
+    void disableDragTarget() {
+        mIsDragTarget = false;
+    }
+
+    boolean isDragTarget() {
+        return mIsDragTarget;
+    }
+
+    void setIsDragOverlapping(boolean isDragOverlapping) {
+        if (mIsDragOverlapping != isDragOverlapping) {
+            mIsDragOverlapping = isDragOverlapping;
+            setUseActiveGlowBackground(mIsDragOverlapping);
+            invalidate();
+        }
+    }
+
     boolean getIsDragOverlapping() {
         return mIsDragOverlapping;
     }
 
-    protected void setOverscrollTransformsDirty(boolean dirty) {
-        mScrollingTransformsDirty = dirty;
-    }
-
-    protected void resetOverscrollTransforms() {
-        if (mScrollingTransformsDirty) {
-            setOverscrollTransformsDirty(false);
-            setTranslationX(0);
-            setRotationY(0);
-            // It doesn't matter if we pass true or false here, the important thing is that we
-            // pass 0, which results in the overscroll drawable not being drawn any more.
-            setOverScrollAmount(0, false);
-            setPivotX(getMeasuredWidth() / 2);
-            setPivotY(getMeasuredHeight() / 2);
-        }
-    }
-
     @Override
     protected void onDraw(Canvas canvas) {
         // When we're large, we are either drawn in a "hover" state (ie when dragging an item to
@@ -440,23 +433,6 @@
             }
         }
 
-        // We draw the pressed or focused BubbleTextView's background in CellLayout because it
-        // requires an expanded clip rect (due to the glow's blur radius)
-        if (mPressedOrFocusedIcon != null) {
-            final int padding = mPressedOrFocusedIcon.getPressedOrFocusedBackgroundPadding();
-            final Bitmap b = mPressedOrFocusedIcon.getPressedOrFocusedBackground();
-            if (b != null) {
-                int offset = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() -
-                        (mCountX * mCellWidth);
-                int left = getPaddingLeft() + (int) Math.ceil(offset / 2f);
-                int top = getPaddingTop();
-                canvas.drawBitmap(b,
-                        mPressedOrFocusedIcon.getLeft() + left - padding,
-                        mPressedOrFocusedIcon.getTop() + top - padding,
-                        null);
-            }
-        }
-
         if (DEBUG_VISUALIZE_OCCUPIED) {
             int[] pt = new int[2];
             ColorDrawable cd = new ColorDrawable(Color.RED);
@@ -964,6 +940,7 @@
     }
 
     public void setBackgroundAlphaMultiplier(float multiplier) {
+
         if (mBackgroundAlphaMultiplier != multiplier) {
             mBackgroundAlphaMultiplier = multiplier;
             invalidate();
@@ -989,10 +966,7 @@
     }
 
     public ShortcutAndWidgetContainer getShortcutsAndWidgets() {
-        if (getChildCount() > 0) {
-            return (ShortcutAndWidgetContainer) getChildAt(0);
-        }
-        return null;
+        return mShortcutsAndWidgets;
     }
 
     public View getChildAt(int x, int y) {
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 80f8dfc..72d33dae 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -96,8 +96,10 @@
         setOnHierarchyChangeListener(this);
 
         final Resources res = getResources();
-        mLeftHoverDrawable = res.getDrawable(R.drawable.page_hover_left_holo);
-        mRightHoverDrawable = res.getDrawable(R.drawable.page_hover_right_holo);
+        mLeftHoverDrawable = res.getDrawable(R.drawable.page_hover_left);
+        mRightHoverDrawable = res.getDrawable(R.drawable.page_hover_right);
+        mLeftHoverDrawableActive = res.getDrawable(R.drawable.page_hover_left_active);
+        mRightHoverDrawableActive = res.getDrawable(R.drawable.page_hover_right_active);
         mBackground = res.getDrawable(R.drawable.apps_customize_bg);
     }
 
@@ -850,8 +852,11 @@
     }
 
     private boolean mInScrollArea;
+    private boolean mShowPageHints;
     private Drawable mLeftHoverDrawable;
     private Drawable mRightHoverDrawable;
+    private Drawable mLeftHoverDrawableActive;
+    private Drawable mRightHoverDrawableActive;
 
     void onEnterScrollArea(int direction) {
         mInScrollArea = true;
@@ -863,6 +868,16 @@
         invalidate();
     }
 
+    void showPageHints() {
+        mShowPageHints = true;
+        invalidate();
+    }
+
+    void hidePageHints() {
+        mShowPageHints = false;
+        invalidate();
+    }
+
     /**
      * Note: this is a reimplementation of View.isLayoutRtl() since that is currently hidden api.
      */
@@ -883,25 +898,32 @@
         super.dispatchDraw(canvas);
 
         // Draw screen hover indicators above children.
-        if (mInScrollArea && !LauncherAppState.getInstance().isScreenLarge()) {
+        if (mShowPageHints) {
             Workspace workspace = mLauncher.getWorkspace();
             int width = getMeasuredWidth();
             Rect childRect = new Rect();
-            getDescendantRectRelativeToSelf(workspace.getChildAt(0), childRect);
+            getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.getChildCount() - 1),
+                    childRect);
 
             int page = workspace.getNextPage();
             final boolean isRtl = isLayoutRtl();
             CellLayout leftPage = (CellLayout) workspace.getChildAt(isRtl ? page + 1 : page - 1);
             CellLayout rightPage = (CellLayout) workspace.getChildAt(isRtl ? page - 1 : page + 1);
 
-            if (leftPage != null && leftPage.getIsDragOverlapping()) {
-                mLeftHoverDrawable.setBounds(0, childRect.top,
-                        mLeftHoverDrawable.getIntrinsicWidth(), childRect.bottom);
-                mLeftHoverDrawable.draw(canvas);
-            } else if (rightPage != null && rightPage.getIsDragOverlapping()) {
-                mRightHoverDrawable.setBounds(width - mRightHoverDrawable.getIntrinsicWidth(),
+            if (leftPage != null && leftPage.isDragTarget()) {
+                Drawable left = mInScrollArea && leftPage.getIsDragOverlapping() ?
+                        mLeftHoverDrawableActive : mLeftHoverDrawable;
+                left.setBounds(0, childRect.top,
+                        left.getIntrinsicWidth(), childRect.bottom);
+                left.draw(canvas);
+            }
+            if (rightPage != null && rightPage.isDragTarget()) {
+                Drawable right = mInScrollArea && rightPage.getIsDragOverlapping() ?
+                        mRightHoverDrawableActive : mRightHoverDrawable;
+
+                right.setBounds(width - right.getIntrinsicWidth(),
                         childRect.top, width, childRect.bottom);
-                mRightHoverDrawable.draw(canvas);
+                right.draw(canvas);
             }
         }
     }
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index cf7c22e..7c9e77e 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -16,30 +16,62 @@
 
 package com.android.launcher3;
 
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.util.SparseArray;
 
 class FastBitmapDrawable extends Drawable {
 
+    static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() {
+
+        @Override
+        public float getInterpolation(float input) {
+            if (input < 0.05f) {
+                return input / 0.05f;
+            } else if (input < 0.3f){
+                return 1;
+            } else {
+                return (1 - input) / 0.7f;
+            }
+        }
+    };
+    static final long CLICK_FEEDBACK_DURATION = 2000;
+
+    private static final int PRESSED_BRIGHTNESS = 100;
     private static ColorMatrix sGhostModeMatrix;
     private static final ColorMatrix sTempMatrix = new ColorMatrix();
 
+    /**
+     * Store the brightness colors filters to optimize animations during icon press. This
+     * only works for non-ghost-mode icons.
+     */
+    private static final SparseArray<ColorFilter> sCachedBrightnessFilter =
+            new SparseArray<ColorFilter>();
+
     private static final int GHOST_MODE_MIN_COLOR_RANGE = 130;
 
     private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
-    private Bitmap mBitmap;
+    private final Bitmap mBitmap;
     private int mAlpha;
 
     private int mBrightness = 0;
     private boolean mGhostModeEnabled = false;
 
+    private boolean mPressed = false;
+    private ObjectAnimator mPressedAnimator;
+
     FastBitmapDrawable(Bitmap b) {
         mAlpha = 255;
         mBitmap = b;
@@ -114,6 +146,23 @@
         }
     }
 
+    public void setPressed(boolean pressed) {
+        if (mPressed != pressed) {
+            mPressed = pressed;
+            if (mPressed) {
+                mPressedAnimator = ObjectAnimator
+                        .ofInt(this, "brightness", PRESSED_BRIGHTNESS)
+                        .setDuration(CLICK_FEEDBACK_DURATION);
+                mPressedAnimator.setInterpolator(CLICK_FEEDBACK_INTERPOLATOR);
+                mPressedAnimator.start();
+            } else if (mPressedAnimator != null) {
+                mPressedAnimator.cancel();
+                setBrightness(0);
+            }
+        }
+        invalidateSelf();
+    }
+
     public boolean isGhostModeEnabled() {
         return mGhostModeEnabled;
     }
@@ -122,14 +171,11 @@
         return mBrightness;
     }
 
-    public void addBrightness(int amount) {
-        setBrightness(mBrightness + amount);
-    }
-
     public void setBrightness(int brightness) {
         if (mBrightness != brightness) {
             mBrightness = brightness;
             updateFilter();
+            invalidateSelf();
         }
     }
 
@@ -157,8 +203,13 @@
                 mPaint.setColorFilter(new ColorMatrixColorFilter(sTempMatrix));
             }
         } else if (mBrightness != 0) {
-            setBrightnessMatrix(sTempMatrix, mBrightness);
-            mPaint.setColorFilter(new ColorMatrixColorFilter(sTempMatrix));
+            ColorFilter filter = sCachedBrightnessFilter.get(mBrightness);
+            if (filter == null) {
+                filter = new PorterDuffColorFilter(Color.argb(mBrightness, 255, 255, 255),
+                        PorterDuff.Mode.SRC_ATOP);
+                sCachedBrightnessFilter.put(mBrightness, filter);
+            }
+            mPaint.setColorFilter(filter);
         } else {
             mPaint.setColorFilter(null);
         }
diff --git a/src/com/android/launcher3/FastBitmapView.java b/src/com/android/launcher3/FastBitmapView.java
new file mode 100644
index 0000000..0937eb7
--- /dev/null
+++ b/src/com/android/launcher3/FastBitmapView.java
@@ -0,0 +1,58 @@
+/*
+ * 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.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.view.View;
+
+public class FastBitmapView extends View {
+
+    private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+    private Bitmap mBitmap;
+
+    public FastBitmapView(Context context) {
+        super(context);
+    }
+
+    /**
+     * Applies the new bitmap.
+     * @return true if the view was invalidated.
+     */
+    public boolean setBitmap(Bitmap b) {
+        if (b != mBitmap){
+            if (mBitmap != null) {
+                invalidate(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+            }
+            mBitmap = b;
+            if (mBitmap != null) {
+                invalidate(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+            }
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mBitmap != null) {
+            canvas.drawBitmap(mBitmap, 0, 0, mPaint);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index df5e0fc..34e752b 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -22,8 +22,6 @@
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.widget.ScrollView;
-import android.widget.TabHost;
-import android.widget.TabWidget;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -89,7 +87,6 @@
 
         final PagedViewGridLayout parent = (PagedViewGridLayout) w.getParent();
         final PagedView container = (PagedView) parent.getParent();
-        final AppsCustomizeTabHost tabHost = findTabHostParent(container);
         final int widgetIndex = parent.indexOfChild(w);
         final int widgetCount = parent.getChildCount();
         final int pageIndex = ((PagedView) container).indexToPage(container.indexOfChild(parent));
@@ -228,6 +225,13 @@
      * Handles key events in a PageViewCellLayout containing PagedViewIcons.
      */
     static boolean handleAppsCustomizeKeyEvent(View v, int keyCode, KeyEvent e) {
+        final int action = e.getAction();
+        if (((action == KeyEvent.ACTION_DOWN) && v.onKeyDown(keyCode, e))
+                || ((action == KeyEvent.ACTION_UP) && v.onKeyUp(keyCode, e))) {
+            // Let the view handle the confirmation key.
+            return true;
+        }
+
         ViewGroup parentLayout;
         ViewGroup itemContainer;
         int countX;
@@ -246,7 +250,6 @@
         // Note we have an extra parent because of the
         // PagedViewCellLayout/PagedViewCellLayoutChildren relationship
         final PagedView container = (PagedView) parentLayout.getParent();
-        final AppsCustomizeTabHost tabHost = findTabHostParent(container);
         final int iconIndex = itemContainer.indexOfChild(v);
         final int itemCount = itemContainer.getChildCount();
         final int pageIndex = ((PagedView) container).indexToPage(container.indexOfChild(parentLayout));
@@ -255,7 +258,6 @@
         final int x = iconIndex % countX;
         final int y = iconIndex / countX;
 
-        final int action = e.getAction();
         final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
         ViewGroup newParent = null;
         // Side pages do not always load synchronously, so check before focusing child siblings
@@ -319,15 +321,6 @@
                 }
                 wasHandled = true;
                 break;
-            case KeyEvent.KEYCODE_ENTER:
-            case KeyEvent.KEYCODE_DPAD_CENTER:
-                if (handleKeyEvent) {
-                    // Simulate a click on the icon
-                    View.OnClickListener clickListener = (View.OnClickListener) container;
-                    clickListener.onClick(v);
-                }
-                wasHandled = true;
-                break;
             case KeyEvent.KEYCODE_PAGE_UP:
                 if (handleKeyEvent) {
                     // Select the first icon on the previous page, or the first icon on this page
diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java
new file mode 100644
index 0000000..12b7a40
--- /dev/null
+++ b/src/com/android/launcher3/FocusIndicatorView.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.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.util.Pair;
+import android.view.View;
+import android.view.ViewParent;
+
+public class FocusIndicatorView extends View implements View.OnFocusChangeListener {
+
+    // It can be any number >0. The view is resized using scaleX and scaleY.
+    static final int DEFAULT_LAYOUT_SIZE = 100;
+    private static final float MIN_VISIBLE_ALPHA = 0.2f;
+
+    private static final int[] sTempPos = new int[2];
+    private static final int[] sTempShift = new int[2];
+
+    private final int[] mIndicatorPos = new int[2];
+    private final int[] mTargetViewPos = new int[2];
+
+    private View mLastFocusedView;
+    private boolean mInitiated;
+
+    private Pair<View, Boolean> mPendingCall;
+
+    public FocusIndicatorView(Context context) {
+        this(context, null);
+    }
+
+    public FocusIndicatorView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setAlpha(0);
+        setBackgroundColor(getResources().getColor(R.color.focused_background));
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+
+        // Redraw if it is already showing. This avoids a bug where the height changes by a small
+        // amount on connecting/disconnecting a bluetooth keyboard.
+        if (mLastFocusedView != null) {
+            mPendingCall = Pair.create(mLastFocusedView, Boolean.TRUE);
+            invalidate();
+        }
+    }
+
+    @Override
+    public void onFocusChange(View v, boolean hasFocus) {
+        mPendingCall = null;
+        if (!mInitiated && (getWidth() == 0)) {
+            // View not yet laid out. Wait until the view is ready to be drawn, so that be can
+            // get the location on screen.
+            mPendingCall = Pair.create(v, hasFocus);
+            invalidate();
+            return;
+        }
+
+        if (!mInitiated) {
+            getLocationRelativeToParentPagedView(this, mIndicatorPos);
+            mInitiated = true;
+        }
+
+        if (hasFocus) {
+            int indicatorWidth = getWidth();
+            int indicatorHeight = getHeight();
+
+            float scaleX = v.getScaleX() * v.getWidth() / indicatorWidth;
+            float scaleY = v.getScaleY() * v.getHeight() / indicatorHeight;
+
+            getLocationRelativeToParentPagedView(v, mTargetViewPos);
+            float x = mTargetViewPos[0] - mIndicatorPos[0] - (1 - scaleX) * indicatorWidth / 2;
+            float y = mTargetViewPos[1] - mIndicatorPos[1] - (1 - scaleY) * indicatorHeight / 2;
+
+            if (getAlpha() > MIN_VISIBLE_ALPHA) {
+                animate()
+                .translationX(x)
+                .translationY(y)
+                .scaleX(scaleX)
+                .scaleY(scaleY)
+                .alpha(1);
+            } else {
+                setTranslationX(x);
+                setTranslationY(y);
+                setScaleX(scaleX);
+                setScaleY(scaleY);
+                animate().alpha(1);
+            }
+            mLastFocusedView = v;
+        } else {
+            if (mLastFocusedView == v) {
+                mLastFocusedView = null;
+                animate().alpha(0);
+            }
+        }
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mPendingCall != null) {
+            onFocusChange(mPendingCall.first, mPendingCall.second);
+        }
+    }
+
+    /**
+     * Gets the location of a view relative in the window, off-setting any shift due to
+     * page view scroll
+     */
+    private static void getLocationRelativeToParentPagedView(View v, int[] pos) {
+        getPagedViewScrollShift(v, sTempShift);
+        v.getLocationInWindow(sTempPos);
+        pos[0] = sTempPos[0] + sTempShift[0];
+        pos[1] = sTempPos[1] + sTempShift[1];
+    }
+
+    private static void getPagedViewScrollShift(View child, int[] shift) {
+        ViewParent parent = child.getParent();
+        if (parent instanceof PagedView) {
+            View parentView = (View) parent;
+            child.getLocationInWindow(sTempPos);
+            shift[0] = parentView.getPaddingLeft() - sTempPos[0];
+            shift[1] = -(int) child.getTranslationY();
+        } else if (parent instanceof View) {
+            getPagedViewScrollShift((View) parent, shift);
+        } else {
+            shift[0] = shift[1] = 0;
+        }
+    }
+}
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index fcedaea..f26f87c 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -18,6 +18,7 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
@@ -40,6 +41,7 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AccelerateInterpolator;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.LinearLayout;
@@ -72,6 +74,8 @@
     private static final int CLOSE_FOLDER_DELAY_MS = 150;
 
     private int mExpandDuration;
+    private int mMaterialExpandDuration;
+    private int mMaterialExpandStagger;
     protected CellLayout mContent;
     private ScrollView mScrollView;
     private final LayoutInflater mInflater;
@@ -112,9 +116,7 @@
     private static String sDefaultFolderName;
     private static String sHintText;
 
-    private int DRAG_MODE_NONE = 0;
-    private int DRAG_MODE_REORDER = 1;
-    private int mDragMode = DRAG_MODE_NONE;
+    private FocusIndicatorView mFocusIndicatorHandler;
 
     // We avoid measuring the scroll view with a 0 width or height, as this
     // results in CellLayout being measured as UNSPECIFIED, which it does
@@ -157,7 +159,9 @@
         mInputMethodManager = (InputMethodManager)
                 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
 
-        mExpandDuration = res.getInteger(R.integer.config_folderAnimDuration);
+        mExpandDuration = res.getInteger(R.integer.config_folderExpandDuration);
+        mMaterialExpandDuration = res.getInteger(R.integer.config_materialFolderExpandDuration);
+        mMaterialExpandStagger = res.getInteger(R.integer.config_materialFolderExpandStagger);
 
         if (sDefaultFolderName == null) {
             sDefaultFolderName = res.getString(R.string.folder_name);
@@ -178,6 +182,11 @@
         mScrollView = (ScrollView) findViewById(R.id.scroll_view);
         mContent = (CellLayout) findViewById(R.id.folder_content);
 
+        mFocusIndicatorHandler = new FocusIndicatorView(getContext());
+        mContent.addView(mFocusIndicatorHandler, 0);
+        mFocusIndicatorHandler.getLayoutParams().height = FocusIndicatorView.DEFAULT_LAYOUT_SIZE;
+        mFocusIndicatorHandler.getLayoutParams().width = FocusIndicatorView.DEFAULT_LAYOUT_SIZE;
+
         LauncherAppState app = LauncherAppState.getInstance();
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
 
@@ -241,7 +250,6 @@
 
             mLauncher.getLauncherClings().dismissFolderCling(null);
 
-            mLauncher.getWorkspace().onDragStartedWithItem(v);
             mLauncher.getWorkspace().beginDragShared(v, this);
 
             mCurrentDragInfo = item;
@@ -443,18 +451,93 @@
         mState = STATE_SMALL;
     }
 
+    private void prepareReveal() {
+        setScaleX(1f);
+        setScaleY(1f);
+        setAlpha(1f);
+        mState = STATE_SMALL;
+    }
+
     public void animateOpen() {
-        positionAndSizeAsIcon();
-
         if (!(getParent() instanceof DragLayer)) return;
-        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);
 
-        oa.addListener(new AnimatorListenerAdapter() {
+        Animator openFolderAnim = null;
+        final Runnable onCompleteRunnable;
+        if (!Utilities.isLmp()) {
+            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);
+            oa.setDuration(mExpandDuration);
+            openFolderAnim = oa;
+
+            setLayerType(LAYER_TYPE_HARDWARE, null);
+            onCompleteRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    setLayerType(LAYER_TYPE_NONE, null);
+                }
+            };
+        } else {
+            prepareReveal();
+            centerAboutIcon();
+
+            int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
+            int height = getFolderHeight();
+
+            float transX = - 0.075f * (width / 2 - getPivotX());
+            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);
+
+            int rx = (int) Math.max(Math.max(width - getPivotX(), 0), getPivotX());
+            int ry = (int) Math.max(Math.max(height - getPivotY(), 0), getPivotY());
+            float radius = (float) Math.sqrt(rx * rx + ry * ry);
+            AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
+            Animator reveal = LauncherAnimUtils.createCircularReveal(this, (int) getPivotX(),
+                    (int) getPivotY(), 0, radius);
+            reveal.setDuration(mMaterialExpandDuration);
+            reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
+
+            mContent.setAlpha(0f);
+            Animator iconsAlpha = LauncherAnimUtils.ofFloat(mContent, "alpha", 0f, 1f);
+            iconsAlpha.setDuration(mMaterialExpandDuration);
+            iconsAlpha.setStartDelay(mMaterialExpandStagger);
+            iconsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
+
+            mFolderName.setAlpha(0f);
+            Animator textAlpha = LauncherAnimUtils.ofFloat(mFolderName, "alpha", 0f, 1f);
+            textAlpha.setDuration(mMaterialExpandDuration);
+            textAlpha.setStartDelay(mMaterialExpandStagger);
+            textAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
+
+            Animator drift = LauncherAnimUtils.ofPropertyValuesHolder(this, tx, ty);
+            drift.setDuration(mMaterialExpandDuration);
+            drift.setStartDelay(mMaterialExpandStagger);
+            drift.setInterpolator(new LogDecelerateInterpolator(60, 0));
+
+            anim.play(drift);
+            anim.play(iconsAlpha);
+            anim.play(textAlpha);
+            anim.play(reveal);
+
+            openFolderAnim = anim;
+
+            mContent.setLayerType(LAYER_TYPE_HARDWARE, null);
+            onCompleteRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    mContent.setLayerType(LAYER_TYPE_NONE, null);
+                }
+            };
+        }
+        openFolderAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
                 sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
@@ -465,7 +548,10 @@
             @Override
             public void onAnimationEnd(Animator animation) {
                 mState = STATE_OPEN;
-                setLayerType(LAYER_TYPE_NONE, null);
+
+                if (onCompleteRunnable != null) {
+                    onCompleteRunnable.run();
+                }
 
                 // Only show cling if we are not in the middle of a drag - this would be quite jarring.
                 if (!mDragController.isDragging()) {
@@ -479,9 +565,7 @@
                 setFocusOnFirstChild();
             }
         });
-        oa.setDuration(mExpandDuration);
-        setLayerType(LAYER_TYPE_HARDWARE, null);
-        oa.start();
+        openFolderAnim.start();
 
         // Make sure the folder picks up the last drag move even if the finger doesn't move.
         if (mDragController.isDragging()) {
@@ -572,6 +656,7 @@
 
         textView.setOnClickListener(this);
         textView.setOnLongClickListener(this);
+        textView.setOnFocusChangeListener(mFocusIndicatorHandler);
 
         // We need to check here to verify that the given item's location isn't already occupied
         // by another item.
@@ -693,9 +778,6 @@
                 mReorderAlarm.setAlarm(REORDER_DELAY);
                 mPreviousTargetCell[0] = mTargetCell[0];
                 mPreviousTargetCell[1] = mTargetCell[1];
-                mDragMode = DRAG_MODE_REORDER;
-            } else {
-                mDragMode = DRAG_MODE_NONE;
             }
         }
     }
@@ -751,7 +833,6 @@
             mOnExitAlarm.setAlarm(ON_EXIT_CLOSE_DELAY);
         }
         mReorderAlarm.cancelAlarm();
-        mDragMode = DRAG_MODE_NONE;
     }
 
     public void onDropCompleted(final View target, final DragObject d,
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index c0b9da7..a359f11 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -178,6 +178,7 @@
         icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon);
         folderInfo.addListener(icon);
 
+        icon.setOnFocusChangeListener(launcher.mFocusHandler);
         return icon;
     }
 
@@ -582,9 +583,10 @@
             d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
             if (d instanceof FastBitmapDrawable) {
                 FastBitmapDrawable fd = (FastBitmapDrawable) d;
-                fd.addBrightness(params.overlayAlpha);
+                int oldBrightness = fd.getBrightness();
+                fd.setBrightness(params.overlayAlpha);
                 d.draw(canvas);
-                fd.addBrightness(-params.overlayAlpha);
+                fd.setBrightness(oldBrightness);
             } else {
                 d.setColorFilter(Color.argb(params.overlayAlpha, 255, 255, 255),
                         PorterDuff.Mode.SRC_ATOP);
diff --git a/src/com/android/launcher3/HolographicOutlineHelper.java b/src/com/android/launcher3/HolographicOutlineHelper.java
index d7b960a..b1e0e68 100644
--- a/src/com/android/launcher3/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/HolographicOutlineHelper.java
@@ -20,48 +20,49 @@
 import android.graphics.Bitmap;
 import android.graphics.BlurMaskFilter;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.Region.Op;
 
 public class HolographicOutlineHelper {
-    private final Paint mHolographicPaint = new Paint();
+
+    private static final Rect sTempRect = new Rect();
+
+    private final Canvas mCanvas = new Canvas();
+    private final Paint mDrawPaint = new Paint();
     private final Paint mBlurPaint = new Paint();
     private final Paint mErasePaint = new Paint();
 
-    public int mMaxOuterBlurRadius;
-    public int mMinOuterBlurRadius;
+    private final BlurMaskFilter mMediumOuterBlurMaskFilter;
+    private final BlurMaskFilter mThinOuterBlurMaskFilter;
+    private final BlurMaskFilter mMediumInnerBlurMaskFilter;
 
-    private BlurMaskFilter mExtraThickOuterBlurMaskFilter;
-    private BlurMaskFilter mThickOuterBlurMaskFilter;
-    private BlurMaskFilter mMediumOuterBlurMaskFilter;
-    private BlurMaskFilter mThinOuterBlurMaskFilter;
-    private BlurMaskFilter mThickInnerBlurMaskFilter;
-    private BlurMaskFilter mExtraThickInnerBlurMaskFilter;
-    private BlurMaskFilter mMediumInnerBlurMaskFilter;
+    private final BlurMaskFilter mShaowBlurMaskFilter;
+    private final int mShadowOffset;
 
-    private static final int THICK = 0;
-    private static final int MEDIUM = 1;
-    private static final int EXTRA_THICK = 2;
+    /**
+     * Padding used when creating shadow bitmap;
+     */
+    final int shadowBitmapPadding;
 
     static HolographicOutlineHelper INSTANCE;
 
     private HolographicOutlineHelper(Context context) {
         final float scale = LauncherAppState.getInstance().getScreenDensity();
 
-        mMinOuterBlurRadius = (int) (scale * 1.0f);
-        mMaxOuterBlurRadius = (int) (scale * 12.0f);
-
-        mExtraThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 12.0f, BlurMaskFilter.Blur.OUTER);
-        mThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.OUTER);
         mMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER);
         mThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER);
-        mExtraThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.NORMAL);
-        mThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL);
         mMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL);
 
-        mHolographicPaint.setFilterBitmap(true);
-        mHolographicPaint.setAntiAlias(true);
+        mShaowBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL);
+        mShadowOffset = (int) (scale * 2.0f);
+        shadowBitmapPadding = (int) (scale * 4.0f);
+
+        mDrawPaint.setFilterBitmap(true);
+        mDrawPaint.setAntiAlias(true);
         mBlurPaint.setFilterBitmap(true);
         mBlurPaint.setAntiAlias(true);
         mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
@@ -77,37 +78,15 @@
     }
 
     /**
-     * Returns the interpolated holographic highlight alpha for the effect we want when scrolling
-     * pages.
-     */
-    public static float highlightAlphaInterpolator(float r) {
-        float maxAlpha = 0.6f;
-        return (float) Math.pow(maxAlpha * (1.0f - r), 1.5f);
-    }
-
-    /**
-     * Returns the interpolated view alpha for the effect we want when scrolling pages.
-     */
-    public static float viewAlphaInterpolator(float r) {
-        final float pivot = 0.95f;
-        if (r < pivot) {
-            return (float) Math.pow(r / pivot, 1.5f);
-        } else {
-            return 1.0f;
-        }
-    }
-
-    /**
      * Applies a more expensive and accurate outline to whatever is currently drawn in a specified
      * bitmap.
      */
     void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
-            int outlineColor, int thickness) {
-        applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, true,
-                thickness);
+            int outlineColor) {
+        applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, true);
     }
     void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
-            int outlineColor, boolean clipAlpha, int thickness) {
+            int outlineColor, boolean clipAlpha) {
 
         // We start by removing most of the alpha channel so as to ignore shadows, and
         // other types of partial transparency when defining the shape of the object
@@ -127,50 +106,18 @@
         Bitmap glowShape = srcDst.extractAlpha();
 
         // calculate the outer blur first
-        BlurMaskFilter outerBlurMaskFilter;
-        switch (thickness) {
-            case EXTRA_THICK:
-                outerBlurMaskFilter = mExtraThickOuterBlurMaskFilter;
-                break;
-            case THICK:
-                outerBlurMaskFilter = mThickOuterBlurMaskFilter;
-                break;
-            case MEDIUM:
-                outerBlurMaskFilter = mMediumOuterBlurMaskFilter;
-                break;
-            default:
-                throw new RuntimeException("Invalid blur thickness");
-        }
-        mBlurPaint.setMaskFilter(outerBlurMaskFilter);
+        mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
         int[] outerBlurOffset = new int[2];
         Bitmap thickOuterBlur = glowShape.extractAlpha(mBlurPaint, outerBlurOffset);
-        if (thickness == EXTRA_THICK) {
-            mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
-        } else {
-            mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
-        }
 
+        mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
         int[] brightOutlineOffset = new int[2];
         Bitmap brightOutline = glowShape.extractAlpha(mBlurPaint, brightOutlineOffset);
 
         // calculate the inner blur
         srcDstCanvas.setBitmap(glowShape);
         srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
-        BlurMaskFilter innerBlurMaskFilter;
-        switch (thickness) {
-            case EXTRA_THICK:
-                innerBlurMaskFilter = mExtraThickInnerBlurMaskFilter;
-                break;
-            case THICK:
-                innerBlurMaskFilter = mThickInnerBlurMaskFilter;
-                break;
-            case MEDIUM:
-                innerBlurMaskFilter = mMediumInnerBlurMaskFilter;
-                break;
-            default:
-                throw new RuntimeException("Invalid blur thickness");
-        }
-        mBlurPaint.setMaskFilter(innerBlurMaskFilter);
+        mBlurPaint.setMaskFilter(mMediumInnerBlurMaskFilter);
         int[] thickInnerBlurOffset = new int[2];
         Bitmap thickInnerBlur = glowShape.extractAlpha(mBlurPaint, thickInnerBlurOffset);
 
@@ -186,16 +133,16 @@
         // draw the inner and outer blur
         srcDstCanvas.setBitmap(srcDst);
         srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
-        mHolographicPaint.setColor(color);
+        mDrawPaint.setColor(color);
         srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
-                mHolographicPaint);
+                mDrawPaint);
         srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1],
-                mHolographicPaint);
+                mDrawPaint);
 
         // draw the bright outline
-        mHolographicPaint.setColor(outlineColor);
+        mDrawPaint.setColor(outlineColor);
         srcDstCanvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1],
-                mHolographicPaint);
+                mDrawPaint);
 
         // cleanup
         srcDstCanvas.setBitmap(null);
@@ -205,25 +152,52 @@
         glowShape.recycle();
     }
 
-    void applyExtraThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
-            int outlineColor) {
-        applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, EXTRA_THICK);
-    }
+    Bitmap createMediumDropShadow(BubbleTextView view) {
+        final Bitmap result = Bitmap.createBitmap(
+                view.getWidth() + shadowBitmapPadding + shadowBitmapPadding,
+                view.getHeight() + shadowBitmapPadding + shadowBitmapPadding + mShadowOffset,
+                Bitmap.Config.ARGB_8888);
 
-    void applyThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
-            int outlineColor) {
-        applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, THICK);
-    }
+        mCanvas.setBitmap(result);
 
-    void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
-            int outlineColor, boolean clipAlpha) {
-        applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, clipAlpha,
-                MEDIUM);
-    }
+        final Rect clipRect = sTempRect;
+        view.getDrawingRect(sTempRect);
+        // adjust the clip rect so that we don't include the text label
+        clipRect.bottom = view.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V
+                + view.getLayout().getLineTop(0);
 
-    void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
-            int outlineColor) {
-        applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, MEDIUM);
-    }
+        // Draw the View into the bitmap.
+        // The translate of scrollX and scrollY is necessary when drawing TextViews, because
+        // they set scrollX and scrollY to large values to achieve centered text
+        mCanvas.save();
+        mCanvas.scale(view.getScaleX(), view.getScaleY(),
+                view.getWidth() / 2 + shadowBitmapPadding,
+                view.getHeight() / 2 + shadowBitmapPadding);
+        mCanvas.translate(-view.getScrollX() + shadowBitmapPadding,
+                -view.getScrollY() + shadowBitmapPadding);
+        mCanvas.clipRect(clipRect, Op.REPLACE);
+        view.draw(mCanvas);
+        mCanvas.restore();
 
+        int[] blurOffst = new int[2];
+        mBlurPaint.setMaskFilter(mShaowBlurMaskFilter);
+        Bitmap blurBitmap = result.extractAlpha(mBlurPaint, blurOffst);
+
+        mCanvas.save();
+        mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
+        mCanvas.translate(blurOffst[0], blurOffst[1]);
+
+        mDrawPaint.setColor(Color.BLACK);
+        mDrawPaint.setAlpha(30);
+        mCanvas.drawBitmap(blurBitmap, 0, 0, mDrawPaint);
+
+        mDrawPaint.setAlpha(60);
+        mCanvas.drawBitmap(blurBitmap, 0, mShadowOffset, mDrawPaint);
+        mCanvas.restore();
+
+        mCanvas.setBitmap(null);
+        blurBitmap.recycle();
+
+        return result;
+    }
 }
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 79cac8f..3d6befa 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -159,6 +159,7 @@
                 allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
                 mLauncher.setAllAppsButton(allAppsButton);
                 allAppsButton.setOnClickListener(mLauncher);
+                allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
             }
 
             // Note: We do this to ensure that the hotseat is always laid out in the orientation of
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 06b7775..76a85ca 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -146,6 +146,10 @@
         return getFullResDefaultActivityIcon();
     }
 
+    public int getFullResIconDpi() {
+        return mIconDpi;
+    }
+
     public Drawable getFullResIcon(ResolveInfo info) {
         return getFullResIcon(info.activityInfo);
     }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8995856..5d9ff1d 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -82,7 +82,6 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnLongClickListener;
-import android.view.ViewAnimationUtils;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -100,6 +99,7 @@
 
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.PagedView.PageSwitchListener;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
@@ -258,7 +258,7 @@
     private View mWeightWatcher;
     private LauncherClings mLauncherClings;
 
-    private AppWidgetManager mAppWidgetManager;
+    private AppWidgetManagerCompat mAppWidgetManager;
     private LauncherAppWidgetHost mAppWidgetHost;
 
     private ItemInfo mPendingAddInfo = new ItemInfo();
@@ -381,6 +381,8 @@
 
     private Stats mStats;
 
+    FocusIndicatorView mFocusHandler;
+
     static boolean isPropertyEnabled(String propertyName) {
         return Log.isLoggable(propertyName, Log.VERBOSE);
     }
@@ -437,7 +439,7 @@
 
         mStats = new Stats(this);
 
-        mAppWidgetManager = AppWidgetManager.getInstance(this);
+        mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
 
         mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
         mAppWidgetHost.startListening();
@@ -1024,10 +1026,6 @@
             // Resets the previous workspace icon press state
             mWaitingForResume.setStayPressed(false);
         }
-        if (mAppsCustomizeContent != null) {
-            // Resets the previous all apps icon press state
-            mAppsCustomizeContent.resetDrawableState();
-        }
 
         // It is possible that widgets can receive updates while launcher is not in the foreground.
         // Consequently, the widgets will be inflated in the orientation of the foreground activity
@@ -1278,6 +1276,7 @@
         final DragController dragController = mDragController;
 
         mLauncherView = findViewById(R.id.launcher);
+        mFocusHandler = (FocusIndicatorView) findViewById(R.id.focus_indicator);
         mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
         mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
         mWorkspace.setPageSwitchListener(this);
@@ -1417,6 +1416,7 @@
         BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
         favorite.applyFromShortcutInfo(info, mIconCache, true);
         favorite.setOnClickListener(this);
+        favorite.setOnFocusChangeListener(mFocusHandler);
         return favorite;
     }
 
@@ -1570,6 +1570,7 @@
         launcherInfo.spanY = spanXY[1];
         launcherInfo.minSpanX = mPendingAddInfo.minSpanX;
         launcherInfo.minSpanY = mPendingAddInfo.minSpanY;
+        launcherInfo.user = mAppWidgetManager.getUser(appWidgetInfo);
 
         LauncherModel.addItemToDatabase(this, launcherInfo,
                 container, screenId, cellXY[0], cellXY[1], false);
@@ -2195,10 +2196,9 @@
             mPendingAddWidgetId = appWidgetId;
 
             // Launch over to configure widget, if needed
-            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
-            intent.setComponent(appWidgetInfo.configure);
-            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
-            Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_APPWIDGET);
+            mAppWidgetManager.startConfigActivity(appWidgetInfo, appWidgetId, this,
+                    mAppWidgetHost, REQUEST_CREATE_APPWIDGET);
+
         } else {
             // Otherwise just add it
             Runnable onComplete = new Runnable() {
@@ -2282,14 +2282,8 @@
             appWidgetId = getAppWidgetHost().allocateAppWidgetId();
             Bundle options = info.bindOptions;
 
-            boolean success = false;
-            if (options != null) {
-                success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
-                        info.componentName, options);
-            } else {
-                success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
-                        info.componentName);
-            }
+            boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
+                    appWidgetId, info.info, options);
             if (success) {
                 addAppWidgetImpl(appWidgetId, info, null, info.info);
             } else {
@@ -2297,6 +2291,8 @@
                 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
                 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
+                mAppWidgetManager.getUser(mPendingAddWidgetInfo)
+                    .addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
                 // TODO: we need to make sure that this accounts for the options bundle.
                 // intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
                 startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
@@ -2447,6 +2443,8 @@
             }
         } else if (v == mAllAppsButton) {
             onClickAllAppsButton(v);
+        } else if (tag instanceof AppInfo) {
+            startAppShortcutOrInfoActivity(v);
         } else if (tag instanceof LauncherAppWidgetInfo) {
             if (v instanceof PendingAppWidgetHostView) {
                 onClickPendingWidget((PendingAppWidgetHostView) v);
@@ -2454,6 +2452,10 @@
         }
     }
 
+    public void onClickPagedViewIcon(View v) {
+        startAppShortcutOrInfoActivity(v);
+    }
+
     public boolean onTouch(View v, MotionEvent event) {
         return false;
     }
@@ -2471,10 +2473,8 @@
                 mPendingAddInfo.copyFrom(info);
                 mPendingAddWidgetId = widgetId;
 
-                Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
-                intent.setComponent(appWidgetInfo.configure);
-                intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, info.appWidgetId);
-                Utilities.startActivityForResultSafely(this, intent, REQUEST_RECONFIGURE_APPWIDGET);
+                AppWidgetManagerCompat.getInstance(this).startConfigActivity(appWidgetInfo,
+                        info.appWidgetId, this, mAppWidgetHost, REQUEST_RECONFIGURE_APPWIDGET);
             }
         }
     }
@@ -2535,17 +2535,6 @@
     }
 
     /**
-     * Event handler for a paged view icon click.
-     * @param v The view that was clicked.
-     * @param appInfo The {link AppInfo} of the view.
-     */
-    public void onClickPagedViewIcon(View v, AppInfo appInfo) {
-        if (LOGD) Log.d(TAG, "onClickPagedViewIcon");
-        startActivitySafely(v, appInfo.intent, appInfo);
-        getStats().recordLaunch(appInfo.intent);
-    }
-
-    /**
      * Event handler for an app shortcut click.
      *
      * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
@@ -2582,7 +2571,7 @@
             builder.setPositiveButton(R.string.abandoned_search,
                     new DialogInterface.OnClickListener() {
                         public void onClick(DialogInterface dialog, int id) {
-                            startAppShortcutActivity(v);
+                            startAppShortcutOrInfoActivity(v);
                         }
                     }
             );
@@ -2599,24 +2588,29 @@
         }
 
         // Start activities
-        startAppShortcutActivity(v);
+        startAppShortcutOrInfoActivity(v);
     }
 
-    private void startAppShortcutActivity(View v) {
+    private void startAppShortcutOrInfoActivity(View v) {
         Object tag = v.getTag();
-        if (!(tag instanceof ShortcutInfo)) {
-            throw new IllegalArgumentException("Input must be a Shortcut");
-        }
-        final ShortcutInfo shortcut = (ShortcutInfo) tag;
-        final Intent intent = shortcut.intent;
+        final ShortcutInfo shortcut;
+        final Intent intent;
+        if (tag instanceof ShortcutInfo) {
+            shortcut = (ShortcutInfo) tag;
+            intent = shortcut.intent;
+            int[] pos = new int[2];
+            v.getLocationOnScreen(pos);
+            intent.setSourceBounds(new Rect(pos[0], pos[1],
+                    pos[0] + v.getWidth(), pos[1] + v.getHeight()));
 
-        int[] pos = new int[2];
-        v.getLocationOnScreen(pos);
-        intent.setSourceBounds(new Rect(pos[0], pos[1],
-                pos[0] + v.getWidth(), pos[1] + v.getHeight()));
+        } else if (tag instanceof AppInfo) {
+            shortcut = null;
+            intent = ((AppInfo) tag).intent;
+        } else {
+            throw new IllegalArgumentException("Input must be a Shortcut or AppInfo");
+        }
 
         boolean success = startActivitySafely(v, intent, tag);
-
         mStats.recordLaunch(intent, shortcut);
 
         if (success && v instanceof BubbleTextView) {
@@ -2910,7 +2904,10 @@
 
         ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
                 scaleX, scaleY);
-        oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
+        if (Utilities.isLmp()) {
+            oa.setInterpolator(new LogDecelerateInterpolator(100, 0));
+        }
+        oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
         oa.start();
     }
 
@@ -2927,7 +2924,7 @@
         copyFolderIconToImage(fi);
         ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
                 scaleX, scaleY);
-        oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
+        oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
         oa.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -3284,8 +3281,7 @@
                 int allAppsButtonSize = LauncherAppState.getInstance().
                         getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
                 float startRadius = isWidgetTray ? 0 : allAppsButtonSize / 2;
-                ValueAnimator reveal = (ValueAnimator)
-                        LauncherAnimUtils.createCircularReveal(revealView, width / 2,
+                Animator reveal = LauncherAnimUtils.createCircularReveal(revealView, width / 2,
                                 height / 2, startRadius, revealRadius);
                 reveal.setDuration(revealDuration);
                 reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
@@ -4377,15 +4373,9 @@
             Bundle options =
                     AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo);
 
-            boolean success = false;
             int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
-            if (options != null) {
-                success = mAppWidgetManager.bindAppWidgetIdIfAllowed(newWidgetId,
-                        appWidgetInfo.provider, options);
-            } else {
-                success = mAppWidgetManager.bindAppWidgetIdIfAllowed(newWidgetId,
-                        appWidgetInfo.provider);
-            }
+            boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
+                    newWidgetId, appWidgetInfo, options);
 
             // TODO consider showing a permission dialog when the widget is clicked.
             if (!success) {
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 64e82c7..1ea562b 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -299,36 +299,26 @@
         Set<String> savedIds = getSavedIdsByType(Key.FAVORITE, in);
         if (DEBUG) Log.d(TAG, "favorite savedIds.size()=" + savedIds.size());
 
-        // Don't backup apps in other profiles for now.
-        UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
-        long userSerialNumber =
-                UserManagerCompat.getInstance(mContext).getSerialNumberForUser(myUserHandle);
-
         // persist things that have changed since the last backup
         ContentResolver cr = mContext.getContentResolver();
+        // Don't backup apps in other profiles for now.
         Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
-                null, null, null);
+                getUserSelectionArg(), null, null);
         Set<String> currentIds = new HashSet<String>(cursor.getCount());
         try {
             cursor.moveToPosition(-1);
             while(cursor.moveToNext()) {
                 final long id = cursor.getLong(ID_INDEX);
-                final long profileId = cursor.getLong(PROFILE_ID_INDEX);
-                if (userSerialNumber == profileId) {
-                    final long updateTime = cursor.getLong(ID_MODIFIED);
-                    Key key = getKey(Key.FAVORITE, id);
-                    keys.add(key);
-                    final String backupKey = keyToBackupKey(key);
-                    currentIds.add(backupKey);
-                    if (!savedIds.contains(backupKey) || updateTime >= in.t) {
-                        byte[] blob = packFavorite(cursor);
-                        writeRowToBackup(key, blob, out, data);
-                    } else {
-                        if (VERBOSE) Log.v(TAG, "favorite " + id + " was too old: " + updateTime);
-                    }
+                final long updateTime = cursor.getLong(ID_MODIFIED);
+                Key key = getKey(Key.FAVORITE, id);
+                keys.add(key);
+                final String backupKey = keyToBackupKey(key);
+                currentIds.add(backupKey);
+                if (!savedIds.contains(backupKey) || updateTime >= in.t) {
+                    byte[] blob = packFavorite(cursor);
+                    writeRowToBackup(key, blob, out, data);
                 } else {
-                    if (VERBOSE) Log.v(TAG, "favorite " + id + " is for other profile: "
-                            + profileId);
+                    if (VERBOSE) Log.v(TAG, "favorite " + id + " was too old: " + updateTime);
                 }
             }
         } finally {
@@ -469,20 +459,19 @@
         }
         final ContentResolver cr = mContext.getContentResolver();
         final int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
+        final UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
 
         // read the old ID set
         Set<String> savedIds = getSavedIdsByType(Key.ICON, in);
         if (DEBUG) Log.d(TAG, "icon savedIds.size()=" + savedIds.size());
 
         // Don't backup apps in other profiles for now.
-        UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
-        long userSerialNumber =
-                UserManagerCompat.getInstance(mContext).getSerialNumberForUser(myUserHandle);
         int startRows = out.rows;
         if (DEBUG) Log.d(TAG, "starting here: " + startRows);
+
         String where = "(" + Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION + " OR " +
                 Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_SHORTCUT + ") AND " +
-                Favorites.PROFILE_ID + "=" + userSerialNumber;
+                getUserSelectionArg();
         Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
                 where, null, null);
         Set<String> currentIds = new HashSet<String>(cursor.getCount());
@@ -617,7 +606,8 @@
 
         int startRows = out.rows;
         if (DEBUG) Log.d(TAG, "starting here: " + startRows);
-        String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPWIDGET;
+        String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPWIDGET + " AND "
+                + getUserSelectionArg();
         Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
                 where, null, null);
         Set<String> currentIds = new HashSet<String>(cursor.getCount());
@@ -1192,6 +1182,11 @@
         return true;
     }
 
+    private String getUserSelectionArg() {
+        return Favorites.PROFILE_ID + '=' + UserManagerCompat.getInstance(mContext)
+                .getSerialNumberForUser(UserHandleCompat.myUserHandle());
+    }
+
     private class KeyParsingException extends Throwable {
         private KeyParsingException(Throwable cause) {
             super(cause);
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 4c9d1a7..bcb4501 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -30,7 +30,6 @@
 import android.content.Intent.ShortcutIconResource;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
-import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
@@ -52,7 +51,7 @@
 import android.util.Log;
 import android.util.Pair;
 
-import com.android.launcher3.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
@@ -3040,11 +3039,11 @@
     public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
         PackageManager packageManager = context.getPackageManager();
         final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
-        widgetsAndShortcuts.addAll(AppWidgetManager.getInstance(context).getInstalledProviders());
+        widgetsAndShortcuts.addAll(AppWidgetManagerCompat.getInstance(context).getAllProviders());
+
         Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
         widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
-        Collections.sort(widgetsAndShortcuts,
-            new LauncherModel.WidgetAndShortcutNameComparator(packageManager));
+        Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context));
         return widgetsAndShortcuts;
     }
 
@@ -3390,44 +3389,6 @@
         return null;
     }
 
-    /**
-     * Returns a list of all the widgets that can handle configuration with a particular mimeType.
-     */
-    List<WidgetMimeTypeHandlerData> resolveWidgetsForMimeType(Context context, String mimeType) {
-        final PackageManager packageManager = context.getPackageManager();
-        final List<WidgetMimeTypeHandlerData> supportedConfigurationActivities =
-            new ArrayList<WidgetMimeTypeHandlerData>();
-
-        final Intent supportsIntent =
-            new Intent(InstallWidgetReceiver.ACTION_SUPPORTS_CLIPDATA_MIMETYPE);
-        supportsIntent.setType(mimeType);
-
-        // Create a set of widget configuration components that we can test against
-        final List<AppWidgetProviderInfo> widgets =
-            AppWidgetManager.getInstance(context).getInstalledProviders();
-        final HashMap<ComponentName, AppWidgetProviderInfo> configurationComponentToWidget =
-            new HashMap<ComponentName, AppWidgetProviderInfo>();
-        for (AppWidgetProviderInfo info : widgets) {
-            configurationComponentToWidget.put(info.configure, info);
-        }
-
-        // Run through each of the intents that can handle this type of clip data, and cross
-        // reference them with the components that are actual configuration components
-        final List<ResolveInfo> activities = packageManager.queryIntentActivities(supportsIntent,
-                PackageManager.MATCH_DEFAULT_ONLY);
-        for (ResolveInfo info : activities) {
-            final ActivityInfo activityInfo = info.activityInfo;
-            final ComponentName infoComponent = new ComponentName(activityInfo.packageName,
-                    activityInfo.name);
-            if (configurationComponentToWidget.containsKey(infoComponent)) {
-                supportedConfigurationActivities.add(
-                        new InstallWidgetReceiver.WidgetMimeTypeHandlerData(info,
-                                configurationComponentToWidget.get(infoComponent)));
-            }
-        }
-        return supportedConfigurationActivities;
-    }
-
     ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
         Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
         String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
@@ -3570,14 +3531,6 @@
             return 0;
         }
     };
-    public static final Comparator<AppWidgetProviderInfo> getWidgetNameComparator() {
-        final Collator collator = Collator.getInstance();
-        return new Comparator<AppWidgetProviderInfo>() {
-            public final int compare(AppWidgetProviderInfo a, AppWidgetProviderInfo b) {
-                return collator.compare(a.label.toString().trim(), b.label.toString().trim());
-            }
-        };
-    }
     static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) {
         if (info.activityInfo != null) {
             return new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
@@ -3618,11 +3571,14 @@
         }
     };
     public static class WidgetAndShortcutNameComparator implements Comparator<Object> {
-        private Collator mCollator;
-        private PackageManager mPackageManager;
-        private HashMap<Object, String> mLabelCache;
-        WidgetAndShortcutNameComparator(PackageManager pm) {
-            mPackageManager = pm;
+        private final AppWidgetManagerCompat mManager;
+        private final PackageManager mPackageManager;
+        private final HashMap<Object, String> mLabelCache;
+        private final Collator mCollator;
+
+        WidgetAndShortcutNameComparator(Context context) {
+            mManager = AppWidgetManagerCompat.getInstance(context);
+            mPackageManager = context.getPackageManager();
             mLabelCache = new HashMap<Object, String>();
             mCollator = Collator.getInstance();
         }
@@ -3631,17 +3587,17 @@
             if (mLabelCache.containsKey(a)) {
                 labelA = mLabelCache.get(a);
             } else {
-                labelA = (a instanceof AppWidgetProviderInfo) ?
-                    ((AppWidgetProviderInfo) a).label :
-                    ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim();
+                labelA = (a instanceof AppWidgetProviderInfo)
+                        ? mManager.loadLabel((AppWidgetProviderInfo) a)
+                        : ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim();
                 mLabelCache.put(a, labelA);
             }
             if (mLabelCache.containsKey(b)) {
                 labelB = mLabelCache.get(b);
             } else {
-                labelB = (b instanceof AppWidgetProviderInfo) ?
-                    ((AppWidgetProviderInfo) b).label :
-                    ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim();
+                labelB = (b instanceof AppWidgetProviderInfo)
+                        ? mManager.loadLabel((AppWidgetProviderInfo) b)
+                        : ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim();
                 mLabelCache.put(b, labelB);
             }
             return mCollator.compare(labelA, labelB);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index af65536..842e0b0 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -1650,8 +1650,10 @@
                 resolved = systemApp;
             }
             final ActivityInfo info = resolved.activityInfo;
-            final Intent intent = buildMainIntent();
-            intent.setComponent(new ComponentName(info.packageName, info.name));
+            final Intent intent = mPackageManager.getLaunchIntentForPackage(info.packageName);
+            if (intent == null) {
+                return -1;
+            }
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                     Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
 
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 0dbcd97..54aa24e 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -74,11 +74,12 @@
     private static final int MIN_LENGTH_FOR_FLING = 25;
 
     protected static final int PAGE_SNAP_ANIMATION_DURATION = 750;
+    protected static final int OVER_SCROLL_PAGE_SNAP_ANIMATION_DURATION = 350;
     protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
     protected static final float NANOTIME_DIV = 1000000000.0f;
 
     private static final float OVERSCROLL_ACCELERATE_FACTOR = 2;
-    private static final float OVERSCROLL_DAMP_FACTOR = 0.14f;
+    private static final float OVERSCROLL_DAMP_FACTOR = 0.07f;
 
     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.
@@ -1601,29 +1602,20 @@
         return f * f * f + 1.0f;
     }
 
-    protected void acceleratedOverScroll(float amount) {
+    protected float acceleratedOverFactor(float amount) {
         int screenSize = getViewportWidth();
 
         // We want to reach the max over scroll effect when the user has
         // over scrolled half the size of the screen
         float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize);
 
-        if (f == 0) return;
+        if (f == 0) return 0;
 
         // Clamp this factor, f, to -1 < f < 1
         if (Math.abs(f) >= 1) {
             f /= Math.abs(f);
         }
-
-        int overScrollAmount = (int) Math.round(f * screenSize);
-        if (amount < 0) {
-            mOverScrollX = overScrollAmount;
-            super.scrollTo(0, getScrollY());
-        } else {
-            mOverScrollX = mMaxScrollX + overScrollAmount;
-            super.scrollTo(mMaxScrollX, getScrollY());
-        }
-        invalidate();
+        return f;
     }
 
     protected void dampedOverScroll(float amount) {
@@ -1642,10 +1634,10 @@
         int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
         if (amount < 0) {
             mOverScrollX = overScrollAmount;
-            super.scrollTo(0, getScrollY());
+            super.scrollTo(mOverScrollX, getScrollY());
         } else {
             mOverScrollX = mMaxScrollX + overScrollAmount;
-            super.scrollTo(mMaxScrollX, getScrollY());
+            super.scrollTo(mOverScrollX, getScrollY());
         }
         invalidate();
     }
@@ -2145,8 +2137,20 @@
         return minDistanceFromScreenCenterIndex;
     }
 
+    protected boolean isInOverScroll() {
+        return (mOverScrollX > mMaxScrollX || mOverScrollX < 0);
+    }
+
+    protected int getPageSnapDuration() {
+        if (isInOverScroll()) {
+            return OVER_SCROLL_PAGE_SNAP_ANIMATION_DURATION;
+        }
+        return PAGE_SNAP_ANIMATION_DURATION;
+
+    }
+
     protected void snapToDestination() {
-        snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
+        snapToPage(getPageNearestToCenterOfScreen(), getPageSnapDuration());
     }
 
     private static class ScrollInterpolator implements Interpolator {
@@ -2177,10 +2181,10 @@
         int delta = newX - mUnboundedScrollX;
         int duration = 0;
 
-        if (Math.abs(velocity) < mMinFlingVelocity) {
+        if (Math.abs(velocity) < mMinFlingVelocity || isInOverScroll()) {
             // If the velocity is low enough, then treat this more as an automatic page advance
             // as opposed to an apparent physical response to flinging
-            snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+            snapToPage(whichPage, getPageSnapDuration());
             return;
         }
 
@@ -2204,11 +2208,11 @@
     }
 
     protected void snapToPage(int whichPage) {
-        snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+        snapToPage(whichPage, getPageSnapDuration());
     }
 
     protected void snapToPageImmediately(int whichPage) {
-        snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true, null);
+        snapToPage(whichPage, getPageSnapDuration(), true, null);
     }
 
     protected void snapToPage(int whichPage, int duration) {
diff --git a/src/com/android/launcher3/PagedViewIcon.java b/src/com/android/launcher3/PagedViewIcon.java
deleted file mode 100644
index e819d5e..0000000
--- a/src/com/android/launcher3/PagedViewIcon.java
+++ /dev/null
@@ -1,114 +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.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Region;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.widget.TextView;
-
-/**
- * An icon on a PagedView, specifically for items in the launcher's paged view (with compound
- * drawables on the top).
- */
-public class PagedViewIcon extends TextView {
-    /** A simple callback interface to allow a PagedViewIcon to notify when it has been pressed */
-    public static interface PressedCallback {
-        void iconPressed(PagedViewIcon icon);
-    }
-
-    @SuppressWarnings("unused")
-    private static final String TAG = "PagedViewIcon";
-    private static final float PRESS_ALPHA = 0.4f;
-
-    private PagedViewIcon.PressedCallback mPressedCallback;
-    private boolean mLockDrawableState = false;
-
-    private Bitmap mIcon;
-
-    public PagedViewIcon(Context context) {
-        this(context, null);
-    }
-
-    public PagedViewIcon(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public PagedViewIcon(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    public void onFinishInflate() {
-        super.onFinishInflate();
-
-        // Ensure we are using the right text size
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-        setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
-    }
-
-    public void applyFromApplicationInfo(AppInfo info, boolean scaleUp,
-            PagedViewIcon.PressedCallback cb) {
-        LauncherAppState app = LauncherAppState.getInstance();
-        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-
-        mIcon = info.iconBitmap;
-        mPressedCallback = cb;
-        Drawable icon = Utilities.createIconDrawable(mIcon);
-        icon.setBounds(0, 0, grid.allAppsIconSizePx, grid.allAppsIconSizePx);
-        setCompoundDrawables(null, icon, null, null);
-        setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
-        setText(info.title);
-        if (info.contentDescription != null) {
-            setContentDescription(info.contentDescription);
-        }
-        setTag(info);
-    }
-
-    public void lockDrawableState() {
-        mLockDrawableState = true;
-    }
-
-    public void resetDrawableState() {
-        mLockDrawableState = false;
-        post(new Runnable() {
-            @Override
-            public void run() {
-                refreshDrawableState();
-            }
-        });
-    }
-
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-
-        // We keep in the pressed state until resetDrawableState() is called to reset the press
-        // feedback
-        if (isPressed()) {
-            setAlpha(PRESS_ALPHA);
-            if (mPressedCallback != null) {
-                mPressedCallback.iconPressed(this);
-            }
-        } else if (!mLockDrawableState) {
-            setAlpha(1f);
-        }
-    }
-}
diff --git a/src/com/android/launcher3/PagedViewWidget.java b/src/com/android/launcher3/PagedViewWidget.java
index db4aeb9..e6e11a3 100644
--- a/src/com/android/launcher3/PagedViewWidget.java
+++ b/src/com/android/launcher3/PagedViewWidget.java
@@ -30,6 +30,8 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+
 /**
  * The linear layout used strictly for the widget/wallpaper tab of the customization tray
  */
@@ -127,7 +129,7 @@
             image.setMaxWidth(maxWidth);
         }
         final TextView name = (TextView) findViewById(R.id.widget_name);
-        name.setText(info.label);
+        name.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
         final TextView dims = (TextView) findViewById(R.id.widget_dims);
         if (dims != null) {
             int hSpan = Math.min(cellSpan[0], (int) grid.numColumns);
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index f70fc4d..f20f261 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -78,7 +78,7 @@
     /**
      * Returns a FastBitmapDrawable with the icon, accurately sized.
      */
-    static FastBitmapDrawable createIconDrawable(Bitmap icon) {
+    public static FastBitmapDrawable createIconDrawable(Bitmap icon) {
         FastBitmapDrawable d = new FastBitmapDrawable(icon);
         d.setFilterBitmap(true);
         resizeIconDrawable(d);
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 1b37700..5aa7190 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -5,7 +5,6 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.database.Cursor;
@@ -29,6 +28,8 @@
 import android.os.AsyncTask;
 import android.util.Log;
 
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
@@ -42,124 +43,123 @@
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 
-abstract class SoftReferenceThreadLocal<T> {
-    private ThreadLocal<SoftReference<T>> mThreadLocal;
-    public SoftReferenceThreadLocal() {
-        mThreadLocal = new ThreadLocal<SoftReference<T>>();
-    }
+public class WidgetPreviewLoader {
 
-    abstract T initialValue();
+    private static abstract class SoftReferenceThreadLocal<T> {
+        private ThreadLocal<SoftReference<T>> mThreadLocal;
+        public SoftReferenceThreadLocal() {
+            mThreadLocal = new ThreadLocal<SoftReference<T>>();
+        }
 
-    public void set(T t) {
-        mThreadLocal.set(new SoftReference<T>(t));
-    }
+        abstract T initialValue();
 
-    public T get() {
-        SoftReference<T> reference = mThreadLocal.get();
-        T obj;
-        if (reference == null) {
-            obj = initialValue();
-            mThreadLocal.set(new SoftReference<T>(obj));
-            return obj;
-        } else {
-            obj = reference.get();
-            if (obj == null) {
+        public void set(T t) {
+            mThreadLocal.set(new SoftReference<T>(t));
+        }
+
+        public T get() {
+            SoftReference<T> reference = mThreadLocal.get();
+            T obj;
+            if (reference == null) {
                 obj = initialValue();
                 mThreadLocal.set(new SoftReference<T>(obj));
+                return obj;
+            } else {
+                obj = reference.get();
+                if (obj == null) {
+                    obj = initialValue();
+                    mThreadLocal.set(new SoftReference<T>(obj));
+                }
+                return obj;
             }
-            return obj;
         }
     }
-}
 
-class CanvasCache extends SoftReferenceThreadLocal<Canvas> {
-    @Override
-    protected Canvas initialValue() {
-        return new Canvas();
+    private static class CanvasCache extends SoftReferenceThreadLocal<Canvas> {
+        @Override
+        protected Canvas initialValue() {
+            return new Canvas();
+        }
     }
-}
 
-class PaintCache extends SoftReferenceThreadLocal<Paint> {
-    @Override
-    protected Paint initialValue() {
-        return null;
+    private static class PaintCache extends SoftReferenceThreadLocal<Paint> {
+        @Override
+        protected Paint initialValue() {
+            return null;
+        }
     }
-}
 
-class BitmapCache extends SoftReferenceThreadLocal<Bitmap> {
-    @Override
-    protected Bitmap initialValue() {
-        return null;
+    private static class BitmapCache extends SoftReferenceThreadLocal<Bitmap> {
+        @Override
+        protected Bitmap initialValue() {
+            return null;
+        }
     }
-}
 
-class RectCache extends SoftReferenceThreadLocal<Rect> {
-    @Override
-    protected Rect initialValue() {
-        return new Rect();
+    private static class RectCache extends SoftReferenceThreadLocal<Rect> {
+        @Override
+        protected Rect initialValue() {
+            return new Rect();
+        }
     }
-}
 
-class BitmapFactoryOptionsCache extends SoftReferenceThreadLocal<BitmapFactory.Options> {
-    @Override
-    protected BitmapFactory.Options initialValue() {
-        return new BitmapFactory.Options();
+    private static class BitmapFactoryOptionsCache extends
+            SoftReferenceThreadLocal<BitmapFactory.Options> {
+        @Override
+        protected BitmapFactory.Options initialValue() {
+            return new BitmapFactory.Options();
+        }
     }
-}
 
-public class WidgetPreviewLoader {
-    static final String TAG = "WidgetPreviewLoader";
-    static final String ANDROID_INCREMENTAL_VERSION_NAME_KEY = "android.incremental.version";
+    private static final String TAG = "WidgetPreviewLoader";
+    private static final String ANDROID_INCREMENTAL_VERSION_NAME_KEY = "android.incremental.version";
+
+    private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f;
+    private static final HashSet<String> sInvalidPackages = new HashSet<String>();
+
+    // Used for drawing shortcut previews
+    private final BitmapCache mCachedShortcutPreviewBitmap = new BitmapCache();
+    private final PaintCache mCachedShortcutPreviewPaint = new PaintCache();
+    private final CanvasCache mCachedShortcutPreviewCanvas = new CanvasCache();
+
+    // Used for drawing widget previews
+    private final CanvasCache mCachedAppWidgetPreviewCanvas = new CanvasCache();
+    private final RectCache mCachedAppWidgetPreviewSrcRect = new RectCache();
+    private final RectCache mCachedAppWidgetPreviewDestRect = new RectCache();
+    private final PaintCache mCachedAppWidgetPreviewPaint = new PaintCache();
+    private final PaintCache mDefaultAppWidgetPreviewPaint = new PaintCache();
+    private final BitmapFactoryOptionsCache mCachedBitmapFactoryOptions = new BitmapFactoryOptionsCache();
+
+    private final HashMap<String, WeakReference<Bitmap>> mLoadedPreviews = new HashMap<>();
+    private final ArrayList<SoftReference<Bitmap>> mUnusedBitmaps = new ArrayList<>();
+
+    private final Context mContext;
+    private final int mAppIconSize;
+    private final IconCache mIconCache;
+    private final AppWidgetManagerCompat mManager;
 
     private int mPreviewBitmapWidth;
     private int mPreviewBitmapHeight;
     private String mSize;
-    private Context mContext;
-    private PackageManager mPackageManager;
     private PagedViewCellLayout mWidgetSpacingLayout;
 
-    // Used for drawing shortcut previews
-    private BitmapCache mCachedShortcutPreviewBitmap = new BitmapCache();
-    private PaintCache mCachedShortcutPreviewPaint = new PaintCache();
-    private CanvasCache mCachedShortcutPreviewCanvas = new CanvasCache();
-
-    // Used for drawing widget previews
-    private CanvasCache mCachedAppWidgetPreviewCanvas = new CanvasCache();
-    private RectCache mCachedAppWidgetPreviewSrcRect = new RectCache();
-    private RectCache mCachedAppWidgetPreviewDestRect = new RectCache();
-    private PaintCache mCachedAppWidgetPreviewPaint = new PaintCache();
-    private PaintCache mDefaultAppWidgetPreviewPaint = new PaintCache();
     private String mCachedSelectQuery;
-    private BitmapFactoryOptionsCache mCachedBitmapFactoryOptions = new BitmapFactoryOptionsCache();
 
-    private int mAppIconSize;
-    private IconCache mIconCache;
-
-    private static final float sWidgetPreviewIconPaddingPercentage = 0.25f;
 
     private CacheDb mDb;
 
-    private final HashMap<String, WeakReference<Bitmap>> mLoadedPreviews;
-    private final ArrayList<SoftReference<Bitmap>> mUnusedBitmaps;
-    private final static HashSet<String> sInvalidPackages;
-
     private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
 
-    static {
-        sInvalidPackages = new HashSet<String>();
-    }
-
     public WidgetPreviewLoader(Context context) {
         LauncherAppState app = LauncherAppState.getInstance();
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
 
         mContext = context;
-        mPackageManager = mContext.getPackageManager();
         mAppIconSize = grid.iconSizePx;
         mIconCache = app.getIconCache();
+        mManager = AppWidgetManagerCompat.getInstance(context);
+
         mDb = app.getWidgetPreviewCacheDb();
-        mLoadedPreviews = new HashMap<String, WeakReference<Bitmap>>();
-        mUnusedBitmaps = new ArrayList<SoftReference<Bitmap>>();
 
         SharedPreferences sp = context.getSharedPreferences(
                 LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
@@ -175,7 +175,7 @@
             editor.commit();
         }
     }
-    
+
     public void recreateDb() {
         LauncherAppState app = LauncherAppState.getInstance();
         app.recreateWidgetPreviewDb();
@@ -328,7 +328,7 @@
         String output;
         if (o instanceof AppWidgetProviderInfo) {
             sb.append(WIDGET_PREFIX);
-            sb.append(((AppWidgetProviderInfo) o).provider.flattenToString());
+            sb.append(((AppWidgetProviderInfo) o).toString());
             output = sb.toString();
             sb.setLength(0);
         } else {
@@ -413,7 +413,7 @@
         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
     }
 
-    public static void removeItemFromDb(final CacheDb cacheDb, final String objectName) {
+    private static void removeItemFromDb(final CacheDb cacheDb, final String objectName) {
         new AsyncTask<Void, Void, Void>() {
             public Void doInBackground(Void ... args) {
                 SQLiteDatabase db = cacheDb.getWritableDatabase();
@@ -473,7 +473,7 @@
         }
     }
 
-    public Bitmap generatePreview(Object info, Bitmap preview) {
+    private Bitmap generatePreview(Object info, Bitmap preview) {
         if (preview != null &&
                 (preview.getWidth() != mPreviewBitmapWidth ||
                 preview.getHeight() != mPreviewBitmapHeight)) {
@@ -491,8 +491,8 @@
         int[] cellSpans = Launcher.getSpanForWidget(mContext, info);
         int maxWidth = maxWidthForWidgetPreview(cellSpans[0]);
         int maxHeight = maxHeightForWidgetPreview(cellSpans[1]);
-        return generateWidgetPreview(info.provider, info.previewImage, info.icon,
-                cellSpans[0], cellSpans[1], maxWidth, maxHeight, preview, null);
+        return generateWidgetPreview(info, cellSpans[0], cellSpans[1],
+                maxWidth, maxHeight, preview, null);
     }
 
     public int maxWidthForWidgetPreview(int spanX) {
@@ -505,22 +505,20 @@
                 mWidgetSpacingLayout.estimateCellHeight(spanY));
     }
 
-    public Bitmap generateWidgetPreview(ComponentName provider, int previewImage,
-            int iconId, int cellHSpan, int cellVSpan, int maxPreviewWidth, int maxPreviewHeight,
-            Bitmap preview, int[] preScaledWidthOut) {
+    public Bitmap generateWidgetPreview(AppWidgetProviderInfo info, int cellHSpan, int cellVSpan,
+            int maxPreviewWidth, int maxPreviewHeight, Bitmap preview, int[] preScaledWidthOut) {
         // Load the preview image if possible
-        String packageName = provider.getPackageName();
         if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE;
         if (maxPreviewHeight < 0) maxPreviewHeight = Integer.MAX_VALUE;
 
         Drawable drawable = null;
-        if (previewImage != 0) {
-            drawable = mPackageManager.getDrawable(packageName, previewImage, null);
+        if (info.previewImage != 0) {
+            drawable = mManager.loadPreview(info);
             if (drawable != null) {
                 drawable = mutateOnMainThread(drawable);
             } else {
                 Log.w(TAG, "Can't load widget preview drawable 0x" +
-                        Integer.toHexString(previewImage) + " for provider: " + provider);
+                        Integer.toHexString(info.previewImage) + " for provider: " + info.provider);
             }
         }
 
@@ -562,21 +560,16 @@
             c.setBitmap(null);
 
             // Draw the icon in the top left corner
-            int minOffset = (int) (mAppIconSize * sWidgetPreviewIconPaddingPercentage);
+            int minOffset = (int) (mAppIconSize * WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE);
             int smallestSide = Math.min(previewWidth, previewHeight);
             float iconScale = Math.min((float) smallestSide
                     / (mAppIconSize + 2 * minOffset), 1f);
 
             try {
-                Drawable icon = null;
-                int hoffset =
-                        (int) ((previewDrawableWidth - mAppIconSize * iconScale) / 2);
-                int yoffset =
-                        (int) ((previewDrawableHeight - mAppIconSize * iconScale) / 2);
-                if (iconId > 0) {
-                    icon = mIconCache.getFullResIcon(packageName, iconId);
-                }
+                Drawable icon = mManager.loadIcon(info, mIconCache);
                 if (icon != null) {
+                    int hoffset = (int) ((previewDrawableWidth - mAppIconSize * iconScale) / 2);
+                    int yoffset = (int) ((previewDrawableHeight - mAppIconSize * iconScale) / 2);
                     icon = mutateOnMainThread(icon);
                     renderDrawableToBitmap(icon, defaultPreview, hoffset,
                             yoffset, (int) (mAppIconSize * iconScale),
@@ -627,7 +620,7 @@
             c.drawBitmap(defaultPreview, src, dest, p);
             c.setBitmap(null);
         }
-        return preview;
+        return mManager.getBadgeBitmap(info, preview);
     }
 
     private Bitmap generateShortcutPreview(
@@ -685,18 +678,10 @@
         return preview;
     }
 
-
-    public static void renderDrawableToBitmap(
-            Drawable d, Bitmap bitmap, int x, int y, int w, int h) {
-        renderDrawableToBitmap(d, bitmap, x, y, w, h, 1f);
-    }
-
     private static void renderDrawableToBitmap(
-            Drawable d, Bitmap bitmap, int x, int y, int w, int h,
-            float scale) {
+            Drawable d, Bitmap bitmap, int x, int y, int w, int h) {
         if (bitmap != null) {
             Canvas c = new Canvas(bitmap);
-            c.scale(scale, scale);
             Rect oldBounds = d.copyBounds();
             d.setBounds(x, y, x + w, y + h);
             d.draw(c);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 733923d..ace5e84 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -27,7 +27,6 @@
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.app.WallpaperManager;
-import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
@@ -47,9 +46,7 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Handler;
-import android.os.Handler.Callback;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.Parcelable;
 import android.support.v4.view.ViewCompat;
 import android.util.AttributeSet;
@@ -76,9 +73,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -212,10 +207,10 @@
 
     private HolographicOutlineHelper mOutlineHelper;
     private Bitmap mDragOutline = null;
-    private final Rect mTempRect = new Rect();
+    private static final Rect sTempRect = new Rect();
     private final int[] mTempXY = new int[2];
     private int[] mTempVisiblePagesRange = new int[2];
-    private boolean mOverscrollTransformsSet;
+    private boolean mOverscrollEffectSet;
     public static final int DRAG_BITMAP_PADDING = 2;
     private boolean mWorkspaceFadeInAdjacentScreens;
 
@@ -241,6 +236,8 @@
     private DropTarget.DragEnforcer mDragEnforcer;
     private float mMaxDistanceForFolderCreation;
 
+    private final Canvas mCanvas = new Canvas();
+
     // Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
     private float mXDown;
     private float mYDown;
@@ -281,6 +278,8 @@
     private int mLastChildCount = -1;
     private float mTransitionProgress;
 
+    float mOverScrollEffect = 0f;
+
     private Runnable mDeferredAction;
     private boolean mDeferDropAfterUninstall;
     private boolean mUninstallSuccessful;
@@ -583,6 +582,7 @@
         CellLayout customScreen = (CellLayout)
                 mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
         customScreen.disableBackground();
+        customScreen.disableDragTarget();
 
         mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen);
         mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID);
@@ -1112,6 +1112,17 @@
         return super.onInterceptTouchEvent(ev);
     }
 
+    @Override
+    public boolean onGenericMotionEvent(MotionEvent event) {
+        // Ignore pointer scroll events if the custom content doesn't allow scrolling.
+        if ((getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID)
+                && (mCustomContentCallbacks != null)
+                && !mCustomContentCallbacks.isScrollingAllowed()) {
+            return false;
+        }
+        return super.onGenericMotionEvent(event);
+    }
+
     protected void reinflateWidgetsIfNecessary() {
         final int clCount = getChildCount();
         for (int i = 0; i < clCount; i++) {
@@ -1581,7 +1592,7 @@
     private void updatePageAlphaValues(int screenCenter) {
         boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
         if (mWorkspaceFadeInAdjacentScreens &&
-                mState == State.NORMAL &&
+                !workspaceInModalState() &&
                 !mIsSwitchingState &&
                 !isInOverscroll) {
             for (int i = numCustomPages(); i < getChildCount(); i++) {
@@ -1590,6 +1601,7 @@
                     float scrollProgress = getScrollProgress(screenCenter, child, i);
                     float alpha = 1 - Math.abs(scrollProgress);
                     child.getShortcutsAndWidgets().setAlpha(alpha);
+                    //child.setBackgroundAlphaMultiplier(1 - alpha);
                 }
             }
         }
@@ -1683,14 +1695,10 @@
         updateStateForCustomContent(screenCenter);
         enableHwLayersOnVisiblePages();
 
-        boolean shouldOverScroll = (mOverScrollX < 0 && (!hasCustomContent() || isLayoutRtl())) ||
-                (mOverScrollX > mMaxScrollX && (!hasCustomContent() || !isLayoutRtl()));
+        boolean shouldOverScroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
 
         if (shouldOverScroll) {
             int index = 0;
-            float pivotX = 0f;
-            final float leftBiasedPivot = 0.25f;
-            final float rightBiasedPivot = 0.75f;
             final int lowerIndex = 0;
             final int upperIndex = getChildCount() - 1;
 
@@ -1698,25 +1706,29 @@
             index = (!isRtl && isLeftPage) || (isRtl && !isLeftPage) ? lowerIndex : upperIndex;
 
             CellLayout cl = (CellLayout) getChildAt(index);
-            float scrollProgress = getScrollProgress(screenCenter, cl, index);
-            cl.setOverScrollAmount(Math.abs(scrollProgress), isLeftPage);
+            float effect = Math.abs(mOverScrollEffect);
+            cl.setOverScrollAmount(Math.abs(effect), isLeftPage);
 
-            if (!mOverscrollTransformsSet) {
-                mOverscrollTransformsSet = true;
-                cl.setOverscrollTransformsDirty(true);
-            }
+            mOverscrollEffectSet = true;
         } else {
-            if (mOverscrollTransformsSet && getChildCount() > 0) {
-                mOverscrollTransformsSet = false;
-                ((CellLayout) getChildAt(0)).resetOverscrollTransforms();
-                ((CellLayout) getChildAt(getChildCount() - 1)).resetOverscrollTransforms();
+            if (mOverscrollEffectSet && getChildCount() > 0) {
+                mOverscrollEffectSet = false;
+                ((CellLayout) getChildAt(0)).setOverScrollAmount(0, false);
+                ((CellLayout) getChildAt(getChildCount() - 1)).setOverScrollAmount(0, false);
             }
         }
     }
 
     @Override
     protected void overScroll(float amount) {
-        acceleratedOverScroll(amount);
+        boolean shouldOverScroll = (amount < 0 && (!hasCustomContent() || isLayoutRtl())) ||
+                (amount > 0 && (!hasCustomContent() || !isLayoutRtl()));
+        if (shouldOverScroll) {
+            dampedOverScroll(amount);
+            mOverScrollEffect = acceleratedOverFactor(amount);
+        } else {
+            mOverScrollEffect = 0;
+        }
     }
 
     protected void onAttachedToWindow() {
@@ -1979,14 +1991,7 @@
     * appearance).
     *
     */
-    public void onDragStartedWithItem(View v) {
-        final Canvas canvas = new Canvas();
-
-        // The outline is used to visualize where the item will land if dropped
-        mDragOutline = createDragOutline(v, canvas, DRAG_BITMAP_PADDING);
-    }
-
-    private Rect getDrawableBounds(Drawable d) {
+    private static Rect getDrawableBounds(Drawable d) {
         Rect bounds = new Rect();
         d.copyBounds(bounds);
         if (bounds.width() == 0 || bounds.height() == 0) {
@@ -2002,8 +2007,6 @@
     }
 
     public void onExternalDragStartedWithItem(View v) {
-        final Canvas canvas = new Canvas();
-
         // Compose a drag bitmap with the view scaled to the icon size
         LauncherAppState app = LauncherAppState.getInstance();
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
@@ -2023,22 +2026,19 @@
         // Compose the bitmap to create the icon from
         Bitmap b = Bitmap.createBitmap(bmpWidth, bmpHeight,
                 Bitmap.Config.ARGB_8888);
-        Canvas c = new Canvas(b);
-        drawDragView(v, c, 0);
-        c.setBitmap(null);
+        mCanvas.setBitmap(b);
+        drawDragView(v, mCanvas, 0);
+        mCanvas.setBitmap(null);
 
         // The outline is used to visualize where the item will land if dropped
-        mDragOutline = createDragOutline(b, canvas, DRAG_BITMAP_PADDING, iconSize, iconSize, true);
+        mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, iconSize, iconSize, true);
     }
 
     public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
-        final Canvas canvas = new Canvas();
-
         int[] size = estimateItemSize(info.spanX, info.spanY, info, false);
 
         // The outline is used to visualize where the item will land if dropped
-        mDragOutline = createDragOutline(b, canvas, DRAG_BITMAP_PADDING, size[0],
-                size[1], clipAlpha);
+        mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, size[0], size[1], clipAlpha);
     }
 
     public void exitWidgetResizeMode() {
@@ -2536,8 +2536,8 @@
      * @param destCanvas the canvas to draw on
      * @param padding the horizontal and vertical padding to use when drawing
      */
-    private void drawDragView(View v, Canvas destCanvas, int padding) {
-        final Rect clipRect = mTempRect;
+    private static void drawDragView(View v, Canvas destCanvas, int padding) {
+        final Rect clipRect = sTempRect;
         v.getDrawingRect(clipRect);
 
         boolean textVisible = false;
@@ -2576,7 +2576,7 @@
      * @param expectedPadding padding to add to the drag view. If a different padding was used
      * its value will be changed
      */
-    public Bitmap createDragBitmap(View v, Canvas canvas, AtomicInteger expectedPadding) {
+    public Bitmap createDragBitmap(View v, AtomicInteger expectedPadding) {
         Bitmap b;
 
         int padding = expectedPadding.get();
@@ -2591,9 +2591,9 @@
                     v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
         }
 
-        canvas.setBitmap(b);
-        drawDragView(v, canvas, padding);
-        canvas.setBitmap(null);
+        mCanvas.setBitmap(b);
+        drawDragView(v, mCanvas, padding);
+        mCanvas.setBitmap(null);
 
         return b;
     }
@@ -2602,15 +2602,15 @@
      * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
      * Responsibility for the bitmap is transferred to the caller.
      */
-    private Bitmap createDragOutline(View v, Canvas canvas, int padding) {
+    private Bitmap createDragOutline(View v, int padding) {
         final int outlineColor = getResources().getColor(R.color.outline_color);
         final Bitmap b = Bitmap.createBitmap(
                 v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
 
-        canvas.setBitmap(b);
-        drawDragView(v, canvas, padding);
-        mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
-        canvas.setBitmap(null);
+        mCanvas.setBitmap(b);
+        drawDragView(v, mCanvas, padding);
+        mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor);
+        mCanvas.setBitmap(null);
         return b;
     }
 
@@ -2618,11 +2618,11 @@
      * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
      * Responsibility for the bitmap is transferred to the caller.
      */
-    private Bitmap createDragOutline(Bitmap orig, Canvas canvas, int padding, int w, int h,
+    private Bitmap createDragOutline(Bitmap orig, int padding, int w, int h,
             boolean clipAlpha) {
         final int outlineColor = getResources().getColor(R.color.outline_color);
         final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
-        canvas.setBitmap(b);
+        mCanvas.setBitmap(b);
 
         Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
         float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
@@ -2634,10 +2634,10 @@
         // center the image
         dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
 
-        canvas.drawBitmap(orig, src, dst, null);
-        mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor,
+        mCanvas.drawBitmap(orig, src, dst, null);
+        mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor,
                 clipAlpha);
-        canvas.setBitmap(null);
+        mCanvas.setBitmap(null);
 
         return b;
     }
@@ -2655,21 +2655,20 @@
         CellLayout layout = (CellLayout) child.getParent().getParent();
         layout.prepareChildForDrag(child);
 
-        child.clearFocus();
-        child.setPressed(false);
-
-        final Canvas canvas = new Canvas();
-
-        // The outline is used to visualize where the item will land if dropped
-        mDragOutline = createDragOutline(child, canvas, DRAG_BITMAP_PADDING);
         beginDragShared(child, this);
     }
 
     public void beginDragShared(View child, DragSource source) {
+        child.clearFocus();
+        child.setPressed(false);
+
+        // The outline is used to visualize where the item will land if dropped
+        mDragOutline = createDragOutline(child, DRAG_BITMAP_PADDING);
+
         mLauncher.onDragStarted(child);
         // The drag bitmap follows the touch point around on the screen
         AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
-        final Bitmap b = createDragBitmap(child, new Canvas(), padding);
+        final Bitmap b = createDragBitmap(child, padding);
 
         final int bmpWidth = b.getWidth();
         final int bmpHeight = b.getHeight();
@@ -2683,7 +2682,7 @@
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
         Point dragVisualizeOffset = null;
         Rect dragRect = null;
-        if (child instanceof BubbleTextView || child instanceof PagedViewIcon) {
+        if (child instanceof BubbleTextView) {
             int iconSize = grid.iconSizePx;
             int top = child.getPaddingTop();
             int left = (bmpWidth - iconSize) / 2;
@@ -2702,7 +2701,7 @@
         // Clear the pressed state if necessary
         if (child instanceof BubbleTextView) {
             BubbleTextView icon = (BubbleTextView) child;
-            icon.clearPressedOrFocusedBackground();
+            icon.clearPressedBackground();
         } else if (child instanceof FolderIcon) {
             // The folder cling isn't flexible enough to be shown in non-default workspace positions
             // Also if they are dragging it a folder, we assume they don't need to see the cling.
@@ -2737,14 +2736,14 @@
 
         // Compose a new drag bitmap that is of the icon size
         AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
-        final Bitmap tmpB = createDragBitmap(child, new Canvas(), padding);
+        final Bitmap tmpB = createDragBitmap(child, padding);
         Bitmap b = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
         Paint p = new Paint();
         p.setFilterBitmap(true);
-        Canvas c = new Canvas(b);
-        c.drawBitmap(tmpB, new Rect(0, 0, tmpB.getWidth(), tmpB.getHeight()),
+        mCanvas.setBitmap(b);
+        mCanvas.drawBitmap(tmpB, new Rect(0, 0, tmpB.getWidth(), tmpB.getHeight()),
                 new Rect(0, 0, iconSize, iconSize), p);
-        c.setBitmap(null);
+        mCanvas.setBitmap(null);
 
         // Find the child's location on the screen
         int bmpWidth = tmpB.getWidth();
@@ -3241,10 +3240,8 @@
         setCurrentDropLayout(layout);
         setCurrentDragOverlappingLayout(layout);
 
-        // Because we don't have space in the Phone UI (the CellLayouts run to the edge) we
-        // don't need to show the outlines
-        if (LauncherAppState.getInstance().isScreenLarge()) {
-            showOutlines();
+        if (!workspaceInModalState()) {
+            mLauncher.getDragLayer().showPageHints();
         }
     }
 
@@ -3319,6 +3316,7 @@
         if (!mIsPageMoving) {
             hideOutlines();
         }
+        mLauncher.getDragLayer().hidePageHints();
     }
 
     void setCurrentDropLayout(CellLayout layout) {
@@ -4019,12 +4017,12 @@
         int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY);
         Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1],
                 Bitmap.Config.ARGB_8888);
-        Canvas c = new Canvas(b);
+        mCanvas.setBitmap(b);
 
         layout.measure(width, height);
         layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
-        layout.draw(c);
-        c.setBitmap(null);
+        layout.draw(mCanvas);
+        mCanvas.setBitmap(null);
         layout.setVisibility(visibility);
         return b;
     }
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
new file mode 100644
index 0000000..57fac7f
--- /dev/null
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -0,0 +1,83 @@
+/*
+ * 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.compat;
+
+import android.app.Activity;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.Utilities;
+
+import java.util.List;
+
+public abstract class AppWidgetManagerCompat {
+
+    private static final Object sInstanceLock = new Object();
+    private static AppWidgetManagerCompat sInstance;
+
+
+    public static AppWidgetManagerCompat getInstance(Context context) {
+        synchronized (sInstanceLock) {
+            // TODO change this to use api version once L gets an API number.
+            if (sInstance == null) {
+                if (Utilities.isLmp()) {
+                    sInstance = new AppWidgetManagerCompatVL(context);
+                } else {
+                    sInstance = new AppWidgetManagerCompatV16(context);
+                }
+            }
+            return sInstance;
+        }
+    }
+
+    final AppWidgetManager mAppWidgetManager;
+    final Context mContext;
+
+    AppWidgetManagerCompat(Context context) {
+        mContext = context;
+        mAppWidgetManager = AppWidgetManager.getInstance(context);
+    }
+
+    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
+        return mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+    }
+
+    public abstract List<AppWidgetProviderInfo> getAllProviders();
+
+    public abstract String loadLabel(AppWidgetProviderInfo info);
+
+    public abstract boolean bindAppWidgetIdIfAllowed(
+            int appWidgetId, AppWidgetProviderInfo info, Bundle options);
+
+    public abstract UserHandleCompat getUser(AppWidgetProviderInfo info);
+
+    public abstract void startConfigActivity(AppWidgetProviderInfo info, int widgetId,
+            Activity activity, AppWidgetHost host, int requestCode);
+
+    public abstract Drawable loadPreview(AppWidgetProviderInfo info);
+
+    public abstract Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache);
+
+    public abstract Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap);
+
+}
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
new file mode 100644
index 0000000..f599f43
--- /dev/null
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
@@ -0,0 +1,90 @@
+/*
+ * 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.compat;
+
+import android.app.Activity;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.Utilities;
+
+import java.util.List;
+
+class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat {
+
+    AppWidgetManagerCompatV16(Context context) {
+        super(context);
+    }
+
+    @Override
+    public List<AppWidgetProviderInfo> getAllProviders() {
+        return mAppWidgetManager.getInstalledProviders();
+    }
+
+    @Override
+    public String loadLabel(AppWidgetProviderInfo info) {
+        return info.label.trim();
+    }
+
+    @Override
+    public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info,
+            Bundle options) {
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider);
+        } else {
+            return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider, options);
+        }
+    }
+
+    @Override
+    public UserHandleCompat getUser(AppWidgetProviderInfo info) {
+        return UserHandleCompat.myUserHandle();
+    }
+
+    @Override
+    public void startConfigActivity(AppWidgetProviderInfo info, int widgetId, Activity activity,
+            AppWidgetHost host, int requestCode) {
+        Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
+        intent.setComponent(info.configure);
+        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
+        Utilities.startActivityForResultSafely(activity, intent, requestCode);
+    }
+
+    @Override
+    public Drawable loadPreview(AppWidgetProviderInfo info) {
+        return mContext.getPackageManager().getDrawable(
+                info.provider.getPackageName(), info.previewImage, null);
+    }
+
+    @Override
+    public Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache) {
+        return cache.getFullResIcon(info.provider.getPackageName(), info.icon);
+    }
+
+    @Override
+    public Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap) {
+        return bitmap;
+    }
+}
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
new file mode 100644
index 0000000..535c74b
--- /dev/null
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -0,0 +1,142 @@
+/*
+ * 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.compat;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.view.View;
+import android.widget.Toast;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@TargetApi(Build.VERSION_CODES.L)
+class AppWidgetManagerCompatVL extends AppWidgetManagerCompat {
+
+    private final UserManager mUserManager;
+    private final PackageManager mPm;
+
+    AppWidgetManagerCompatVL(Context context) {
+        super(context);
+        mPm = context.getPackageManager();
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+    }
+
+    @Override
+    public List<AppWidgetProviderInfo> getAllProviders() {
+        ArrayList<AppWidgetProviderInfo> providers = new ArrayList<AppWidgetProviderInfo>();
+        for (UserHandle user : mUserManager.getUserProfiles()) {
+            providers.addAll(mAppWidgetManager.getInstalledProvidersForProfile(user));
+        }
+        return providers;
+    }
+
+    @Override
+    public String loadLabel(AppWidgetProviderInfo info) {
+        return info.loadLabel(mPm);
+    }
+
+    @Override
+    public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info,
+            Bundle options) {
+        return mAppWidgetManager.bindAppWidgetIdIfAllowed(
+                appWidgetId, info.getProfile(), info.provider, options);
+    }
+
+    @Override
+    public UserHandleCompat getUser(AppWidgetProviderInfo info) {
+        return UserHandleCompat.fromUser(info.getProfile());
+    }
+
+    @Override
+    public void startConfigActivity(AppWidgetProviderInfo info, int widgetId, Activity activity,
+            AppWidgetHost host, int requestCode) {
+        try {
+            host.startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null);
+        } catch (ActivityNotFoundException e) {
+            Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+        } catch (SecurityException e) {
+            Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+        }
+    }
+
+    @Override
+    public Drawable loadPreview(AppWidgetProviderInfo info) {
+        return info.loadPreviewImage(mContext, 0);
+    }
+
+    @Override
+    public Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache) {
+        return info.loadIcon(mContext, cache.getFullResIconDpi());
+    }
+
+    @Override
+    public Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap) {
+        if (info.getProfile().equals(android.os.Process.myUserHandle())) {
+            return bitmap;
+        }
+
+        // Add a user badge in the bottom right of the image.
+        final Resources res = mContext.getResources();
+        final int badgeSize = res.getDimensionPixelSize(R.dimen.profile_badge_size);
+        final int badgeMargin = res.getDimensionPixelSize(R.dimen.profile_badge_margin);
+        final Rect badgeLocation = new Rect(0, 0, badgeSize, badgeSize);
+
+        final int top = bitmap.getHeight() - badgeSize - badgeMargin;
+        if (res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+            badgeLocation.offset(badgeMargin, top);
+        } else {
+            badgeLocation.offset(bitmap.getWidth() - badgeSize - badgeMargin, top);
+        }
+
+        UserManager userManager = (UserManager) mContext.getSystemService(
+                Context.USER_SERVICE);
+
+        Drawable drawable = userManager.getBadgedDrawableForUser(new BitmapDrawable(res, bitmap),
+                info.getProfile(), badgeLocation, 0);
+
+        if (drawable instanceof BitmapDrawable) {
+            return ((BitmapDrawable) drawable).getBitmap();
+        }
+
+        bitmap.eraseColor(Color.TRANSPARENT);
+        Canvas c = new Canvas(bitmap);
+        drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
+        drawable.draw(c);
+        c.setBitmap(null);
+        return bitmap;
+    }
+}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 26e1b7c..c78ab99 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -17,9 +17,9 @@
 package com.android.launcher3.compat;
 
 import android.content.Context;
-import android.content.pm.InstallSessionInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionCallback;
+import android.content.pm.PackageInstaller.SessionInfo;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -33,7 +33,7 @@
     private static final String TAG = "PackageInstallerCompatVL";
     private static final boolean DEBUG = false;
 
-    private final SparseArray<InstallSessionInfo> mPendingReplays = new SparseArray<InstallSessionInfo>();
+    private final SparseArray<SessionInfo> mPendingReplays = new SparseArray<SessionInfo>();
     private final PackageInstaller mInstaller;
 
     private boolean mResumed;
@@ -47,7 +47,7 @@
 
         mInstaller.addSessionCallback(mCallback);
         // On start, send updates for all active sessions
-        for (InstallSessionInfo info : mInstaller.getAllSessions()) {
+        for (SessionInfo info : mInstaller.getAllSessions()) {
             mPendingReplays.append(info.getSessionId(), info);
         }
     }
@@ -102,7 +102,7 @@
             updates.add(newInfo);
         }
         for (int i = mPendingReplays.size() - 1; i > 0; i--) {
-            InstallSessionInfo session = mPendingReplays.valueAt(i);
+            SessionInfo session = mPendingReplays.valueAt(i);
             if (session.getAppPackageName() != null) {
                 updates.add(new PackageInstallInfo(session.getAppPackageName(),
                         ShortcutInfo.PACKAGE_STATE_INSTALLING,
@@ -119,7 +119,7 @@
 
         @Override
         public void onCreated(int sessionId) {
-            InstallSessionInfo session = mInstaller.getSessionInfo(sessionId);
+            SessionInfo session = mInstaller.getSessionInfo(sessionId);
             if (session != null) {
                 mPendingReplays.put(sessionId, session);
                 replayUpdates(null);
@@ -129,7 +129,7 @@
         @Override
         public void onFinished(int sessionId, boolean success) {
             mPendingReplays.remove(sessionId);
-            InstallSessionInfo session = mInstaller.getSessionInfo(sessionId);
+            SessionInfo session = mInstaller.getSessionInfo(sessionId);
             if ((session != null) && (session.getAppPackageName() != null)) {
                 // Replay all updates with a one time update for this installed package. No
                 // need to store this record for future updates, as the app list will get
@@ -142,7 +142,7 @@
 
         @Override
         public void onProgressChanged(int sessionId, float progress) {
-            InstallSessionInfo session = mInstaller.getSessionInfo(sessionId);
+            SessionInfo session = mInstaller.getSessionInfo(sessionId);
             if (session != null) {
                 mPendingReplays.put(sessionId, session);
                 replayUpdates(null);