Merge "Removing itemCount, fromIndex and toIndex from AccessibilityEvent.TYPE_VIEW_SCROLLED sent by PagedView. This causes an additional reduntant voice message on scroll (see the bug)." into ub-launcher3-burnaby
diff --git a/WallpaperPicker/src/com/android/gallery3d/exif/ExifInterface.java b/WallpaperPicker/src/com/android/gallery3d/exif/ExifInterface.java
index a1cf0fc..9247e87 100644
--- a/WallpaperPicker/src/com/android/gallery3d/exif/ExifInterface.java
+++ b/WallpaperPicker/src/com/android/gallery3d/exif/ExifInterface.java
@@ -1247,7 +1247,7 @@
         if (l == null || l.length <= 0) {
             return null;
         }
-        return new Long(l[0]);
+        return Long.valueOf(l[0]);
     }
 
     /**
@@ -1266,7 +1266,7 @@
         if (l == null || l.length <= 0) {
             return null;
         }
-        return new Integer(l[0]);
+        return Integer.valueOf(l[0]);
     }
 
     /**
@@ -1285,7 +1285,7 @@
         if (l == null || l.length <= 0) {
             return null;
         }
-        return new Byte(l[0]);
+        return Byte.valueOf(l[0]);
     }
 
     /**
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
index 142a9cb..ee0d8b0 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
@@ -131,7 +131,7 @@
 
         // Load image in background
         final BitmapRegionTileSource.UriBitmapSource bitmapSource =
-                new BitmapRegionTileSource.UriBitmapSource(getContext(), imageUri, 1024);
+                new BitmapRegionTileSource.UriBitmapSource(getContext(), imageUri);
         mSetWallpaperButton.setEnabled(false);
         Runnable onLoad = new Runnable() {
             public void run() {
@@ -190,7 +190,7 @@
                     }
                 });
             } catch (SecurityException securityException) {
-                if (isDestroyed()) {
+                if (isActivityDestroyed()) {
                     // Temporarily granted permissions are revoked when the activity
                     // finishes, potentially resulting in a SecurityException here.
                     // Even though {@link #isDestroyed} might also return true in different
@@ -221,6 +221,12 @@
         return false;
     }
 
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+    protected boolean isActivityDestroyed() {
+        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
+                && isDestroyed();
+    }
+
     @Thunk void addReusableBitmap(TileSource src) {
         synchronized (mReusableBitmaps) {
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index 9332091..72cb194 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -21,7 +21,6 @@
 import android.app.ActionBar;
 import android.app.Activity;
 import android.app.WallpaperManager;
-import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -144,8 +143,7 @@
         public void onClick(final WallpaperPickerActivity a) {
             a.setWallpaperButtonEnabled(false);
             final BitmapRegionTileSource.UriBitmapSource bitmapSource =
-                    new BitmapRegionTileSource.UriBitmapSource(
-                            a.getContext(), mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE);
+                    new BitmapRegionTileSource.UriBitmapSource(a.getContext(), mUri);
             a.setCropViewTileSource(bitmapSource, true, false, null, new Runnable() {
 
                 @Override
@@ -199,8 +197,7 @@
         public void onClick(final WallpaperPickerActivity a) {
             a.setWallpaperButtonEnabled(false);
             BitmapRegionTileSource.UriBitmapSource bitmapSource =
-                    new BitmapRegionTileSource.UriBitmapSource(a.getContext(),
-                            Uri.fromFile(mFile), 1024);
+                    new BitmapRegionTileSource.UriBitmapSource(a.getContext(), Uri.fromFile(mFile));
             a.setCropViewTileSource(bitmapSource, false, true, null, new Runnable() {
 
                 @Override
@@ -236,8 +233,7 @@
         public void onClick(final WallpaperPickerActivity a) {
             a.setWallpaperButtonEnabled(false);
             BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
-                    new BitmapRegionTileSource.ResourceBitmapSource(
-                            mResources, mResId, BitmapRegionTileSource.MAX_PREVIEW_SIZE);
+                    new BitmapRegionTileSource.ResourceBitmapSource(mResources, mResId);
             a.setCropViewTileSource(bitmapSource, false, false, new CropViewScaleProvider() {
 
                 @Override
@@ -665,7 +661,7 @@
     }
 
     @Thunk void initializeScrollForRtl() {
-        if (mWallpaperScrollContainer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+        if (Utilities.isRtl(getResources())) {
             final ViewTreeObserver observer = mWallpaperScrollContainer.getViewTreeObserver();
             observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                 public void onGlobalLayout() {
@@ -838,7 +834,7 @@
                     int rotation = BitmapUtils.getRotationFromExif(context, uri);
                     return createThumbnail(defaultSize, context, uri, null, null, 0, rotation, false);
                 } catch (SecurityException securityException) {
-                    if (isDestroyed()) {
+                    if (isActivityDestroyed()) {
                         // Temporarily granted permissions are revoked when the activity
                         // finishes, potentially resulting in a SecurityException here.
                         // Even though {@link #isDestroyed} might also return true in different
diff --git a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
index 15f97e5..2d496a5 100644
--- a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
+++ b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
@@ -26,6 +26,7 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.net.Uri;
+import android.opengl.GLUtils;
 import android.os.Build;
 import android.util.Log;
 
@@ -149,18 +150,15 @@
     private static final int GL_SIZE_LIMIT = 2048;
     // This must be no larger than half the size of the GL_SIZE_LIMIT
     // due to decodePreview being allowed to be up to 2x the size of the target
-    public static final int MAX_PREVIEW_SIZE = GL_SIZE_LIMIT / 2;
+    private static final int MAX_PREVIEW_SIZE = GL_SIZE_LIMIT / 2;
 
     public static abstract class BitmapSource {
         private SimpleBitmapRegionDecoder mDecoder;
         private Bitmap mPreview;
-        private final int mPreviewSize;
         private int mRotation;
         public enum State { NOT_LOADED, LOADED, ERROR_LOADING };
         private State mState = State.NOT_LOADED;
-        public BitmapSource(int previewSize) {
-            mPreviewSize = Math.min(previewSize, MAX_PREVIEW_SIZE);
-        }
+
         public boolean loadInBackground(InBitmapProvider bitmapProvider) {
             ExifInterface ei = new ExifInterface();
             if (readExif(ei)) {
@@ -176,36 +174,44 @@
             } else {
                 int width = mDecoder.getWidth();
                 int height = mDecoder.getHeight();
-                if (mPreviewSize != 0) {
-                    BitmapFactory.Options opts = new BitmapFactory.Options();
-                    opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
-                    opts.inPreferQualityOverSpeed = true;
 
-                    float scale = (float) mPreviewSize / Math.max(width, height);
-                    opts.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
-                    opts.inJustDecodeBounds = false;
-                    opts.inMutable = true;
+                BitmapFactory.Options opts = new BitmapFactory.Options();
+                opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
+                opts.inPreferQualityOverSpeed = true;
 
-                    if (bitmapProvider != null) {
-                        int expectedPixles = (width / opts.inSampleSize) * (height / opts.inSampleSize);
-                        Bitmap reusableBitmap = bitmapProvider.forPixelCount(expectedPixles);
-                        if (reusableBitmap != null) {
-                            // Try loading with reusable bitmap
-                            opts.inBitmap = reusableBitmap;
-                            try {
-                                mPreview = loadPreviewBitmap(opts);
-                            } catch (IllegalArgumentException e) {
-                                Log.d(TAG, "Unable to reusage bitmap", e);
-                                opts.inBitmap = null;
-                                mPreview = null;
-                            }
+                float scale = (float) MAX_PREVIEW_SIZE / Math.max(width, height);
+                opts.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
+                opts.inJustDecodeBounds = false;
+                opts.inMutable = true;
+
+                if (bitmapProvider != null) {
+                    int expectedPixles = (width / opts.inSampleSize) * (height / opts.inSampleSize);
+                    Bitmap reusableBitmap = bitmapProvider.forPixelCount(expectedPixles);
+                    if (reusableBitmap != null) {
+                        // Try loading with reusable bitmap
+                        opts.inBitmap = reusableBitmap;
+                        try {
+                            mPreview = loadPreviewBitmap(opts);
+                        } catch (IllegalArgumentException e) {
+                            Log.d(TAG, "Unable to reusage bitmap", e);
+                            opts.inBitmap = null;
+                            mPreview = null;
                         }
                     }
-                    if (mPreview == null) {
-                        mPreview = loadPreviewBitmap(opts);
-                    }
                 }
-                mState = State.LOADED;
+                if (mPreview == null) {
+                    mPreview = loadPreviewBitmap(opts);
+                }
+
+                // Verify that the bitmap can be used on GL surface
+                try {
+                    GLUtils.getInternalFormat(mPreview);
+                    GLUtils.getType(mPreview);
+                    mState = State.LOADED;
+                } catch (IllegalArgumentException e) {
+                    Log.d(TAG, "Image cannot be rendered on a GL surface", e);
+                    mState = State.ERROR_LOADING;
+                }
                 return true;
             }
         }
@@ -237,8 +243,7 @@
 
     public static class FilePathBitmapSource extends BitmapSource {
         private String mPath;
-        public FilePathBitmapSource(String path, int previewSize) {
-            super(previewSize);
+        public FilePathBitmapSource(String path) {
             mPath = path;
         }
         @Override
@@ -272,8 +277,7 @@
     public static class UriBitmapSource extends BitmapSource {
         private Context mContext;
         private Uri mUri;
-        public UriBitmapSource(Context context, Uri uri, int previewSize) {
-            super(previewSize);
+        public UriBitmapSource(Context context, Uri uri) {
             mContext = context;
             mUri = uri;
         }
@@ -337,8 +341,7 @@
     public static class ResourceBitmapSource extends BitmapSource {
         private Resources mRes;
         private int mResId;
-        public ResourceBitmapSource(Resources res, int resId, int previewSize) {
-            super(previewSize);
+        public ResourceBitmapSource(Resources res, int resId) {
             mRes = res;
             mResId = resId;
         }
diff --git a/res/drawable-hdpi/bg_appwidget_error.9.png b/res/drawable-hdpi/bg_appwidget_error.9.png
deleted file mode 100644
index 4da3195..0000000
--- a/res/drawable-hdpi/bg_appwidget_error.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_widget_resize_handle.png b/res/drawable-hdpi/ic_widget_resize_handle.png
new file mode 100644
index 0000000..844f3cf
--- /dev/null
+++ b/res/drawable-hdpi/ic_widget_resize_handle.png
Binary files differ
diff --git a/res/drawable-hdpi/widget_resize_frame.9.png b/res/drawable-hdpi/widget_resize_frame.9.png
new file mode 100644
index 0000000..5772672
--- /dev/null
+++ b/res/drawable-hdpi/widget_resize_frame.9.png
Binary files differ
diff --git a/res/drawable-hdpi/widget_resize_frame_holo.9.png b/res/drawable-hdpi/widget_resize_frame_holo.9.png
deleted file mode 100644
index 2d6fcf5..0000000
--- a/res/drawable-hdpi/widget_resize_frame_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/widget_resize_handle_bottom.png b/res/drawable-hdpi/widget_resize_handle_bottom.png
deleted file mode 100644
index f0afd61..0000000
--- a/res/drawable-hdpi/widget_resize_handle_bottom.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/widget_resize_handle_left.png b/res/drawable-hdpi/widget_resize_handle_left.png
deleted file mode 100644
index 47613b2..0000000
--- a/res/drawable-hdpi/widget_resize_handle_left.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/widget_resize_handle_right.png b/res/drawable-hdpi/widget_resize_handle_right.png
deleted file mode 100644
index acc28be..0000000
--- a/res/drawable-hdpi/widget_resize_handle_right.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/widget_resize_handle_top.png b/res/drawable-hdpi/widget_resize_handle_top.png
deleted file mode 100644
index 2c60be0..0000000
--- a/res/drawable-hdpi/widget_resize_handle_top.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/widget_resize_shadow.9.png b/res/drawable-hdpi/widget_resize_shadow.9.png
new file mode 100644
index 0000000..a67da6e
--- /dev/null
+++ b/res/drawable-hdpi/widget_resize_shadow.9.png
Binary files differ
diff --git a/res/drawable-mdpi/bg_appwidget_error.9.png b/res/drawable-mdpi/bg_appwidget_error.9.png
deleted file mode 100644
index 493c0d4..0000000
--- a/res/drawable-mdpi/bg_appwidget_error.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_widget_resize_handle.png b/res/drawable-mdpi/ic_widget_resize_handle.png
new file mode 100644
index 0000000..c3b287f
--- /dev/null
+++ b/res/drawable-mdpi/ic_widget_resize_handle.png
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_frame.9.png b/res/drawable-mdpi/widget_resize_frame.9.png
new file mode 100644
index 0000000..8bc8a5c
--- /dev/null
+++ b/res/drawable-mdpi/widget_resize_frame.9.png
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_frame_holo.9.png b/res/drawable-mdpi/widget_resize_frame_holo.9.png
deleted file mode 100644
index 028bd62..0000000
--- a/res/drawable-mdpi/widget_resize_frame_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_handle_bottom.png b/res/drawable-mdpi/widget_resize_handle_bottom.png
deleted file mode 100644
index c838bf4..0000000
--- a/res/drawable-mdpi/widget_resize_handle_bottom.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_handle_left.png b/res/drawable-mdpi/widget_resize_handle_left.png
deleted file mode 100644
index ff0b0d3..0000000
--- a/res/drawable-mdpi/widget_resize_handle_left.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_handle_right.png b/res/drawable-mdpi/widget_resize_handle_right.png
deleted file mode 100644
index fc4808e..0000000
--- a/res/drawable-mdpi/widget_resize_handle_right.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_handle_top.png b/res/drawable-mdpi/widget_resize_handle_top.png
deleted file mode 100644
index 3b1df01..0000000
--- a/res/drawable-mdpi/widget_resize_handle_top.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_shadow.9.png b/res/drawable-mdpi/widget_resize_shadow.9.png
new file mode 100644
index 0000000..2bae2b6
--- /dev/null
+++ b/res/drawable-mdpi/widget_resize_shadow.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/bg_appwidget_error.9.png b/res/drawable-xhdpi/bg_appwidget_error.9.png
deleted file mode 100644
index b792cc8..0000000
--- a/res/drawable-xhdpi/bg_appwidget_error.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_widget_resize_handle.png b/res/drawable-xhdpi/ic_widget_resize_handle.png
new file mode 100644
index 0000000..f445a1c
--- /dev/null
+++ b/res/drawable-xhdpi/ic_widget_resize_handle.png
Binary files differ
diff --git a/res/drawable-xhdpi/widget_resize_frame.9.png b/res/drawable-xhdpi/widget_resize_frame.9.png
new file mode 100644
index 0000000..e6cf0af
--- /dev/null
+++ b/res/drawable-xhdpi/widget_resize_frame.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/widget_resize_frame_holo.9.png b/res/drawable-xhdpi/widget_resize_frame_holo.9.png
deleted file mode 100644
index 76cec60..0000000
--- a/res/drawable-xhdpi/widget_resize_frame_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/widget_resize_handle_bottom.png b/res/drawable-xhdpi/widget_resize_handle_bottom.png
deleted file mode 100644
index 19437d7..0000000
--- a/res/drawable-xhdpi/widget_resize_handle_bottom.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/widget_resize_handle_left.png b/res/drawable-xhdpi/widget_resize_handle_left.png
deleted file mode 100644
index 28c5487..0000000
--- a/res/drawable-xhdpi/widget_resize_handle_left.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/widget_resize_handle_right.png b/res/drawable-xhdpi/widget_resize_handle_right.png
deleted file mode 100644
index 4f672a6..0000000
--- a/res/drawable-xhdpi/widget_resize_handle_right.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/widget_resize_handle_top.png b/res/drawable-xhdpi/widget_resize_handle_top.png
deleted file mode 100644
index e866c00..0000000
--- a/res/drawable-xhdpi/widget_resize_handle_top.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/widget_resize_shadow.9.png b/res/drawable-xhdpi/widget_resize_shadow.9.png
new file mode 100644
index 0000000..99e9e78
--- /dev/null
+++ b/res/drawable-xhdpi/widget_resize_shadow.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_widget_resize_handle.png b/res/drawable-xxhdpi/ic_widget_resize_handle.png
new file mode 100644
index 0000000..144cac9
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_widget_resize_handle.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_frame.9.png b/res/drawable-xxhdpi/widget_resize_frame.9.png
new file mode 100644
index 0000000..7a49d01
--- /dev/null
+++ b/res/drawable-xxhdpi/widget_resize_frame.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_frame_holo.9.png b/res/drawable-xxhdpi/widget_resize_frame_holo.9.png
deleted file mode 100644
index 1681387..0000000
--- a/res/drawable-xxhdpi/widget_resize_frame_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_handle_bottom.png b/res/drawable-xxhdpi/widget_resize_handle_bottom.png
deleted file mode 100644
index d549fcd..0000000
--- a/res/drawable-xxhdpi/widget_resize_handle_bottom.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_handle_left.png b/res/drawable-xxhdpi/widget_resize_handle_left.png
deleted file mode 100644
index dd56dad..0000000
--- a/res/drawable-xxhdpi/widget_resize_handle_left.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_handle_right.png b/res/drawable-xxhdpi/widget_resize_handle_right.png
deleted file mode 100644
index 296a1c1..0000000
--- a/res/drawable-xxhdpi/widget_resize_handle_right.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_handle_top.png b/res/drawable-xxhdpi/widget_resize_handle_top.png
deleted file mode 100644
index e86270a..0000000
--- a/res/drawable-xxhdpi/widget_resize_handle_top.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_shadow.9.png b/res/drawable-xxhdpi/widget_resize_shadow.9.png
new file mode 100644
index 0000000..ae0f564
--- /dev/null
+++ b/res/drawable-xxhdpi/widget_resize_shadow.9.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_widget_resize_handle.png b/res/drawable-xxxhdpi/ic_widget_resize_handle.png
new file mode 100644
index 0000000..4bde6b9
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_widget_resize_handle.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/widget_resize_frame.9.png b/res/drawable-xxxhdpi/widget_resize_frame.9.png
new file mode 100644
index 0000000..9b711f2
--- /dev/null
+++ b/res/drawable-xxxhdpi/widget_resize_frame.9.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/widget_resize_shadow.9.png b/res/drawable-xxxhdpi/widget_resize_shadow.9.png
new file mode 100644
index 0000000..defb311
--- /dev/null
+++ b/res/drawable-xxxhdpi/widget_resize_shadow.9.png
Binary files differ
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index d5dd91a..f0f7bbb 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -18,7 +18,6 @@
 <com.android.launcher3.LauncherRootView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto"
-
     android:id="@+id/launcher"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
diff --git a/res/layout-land/migration_cling.xml b/res/layout-land/migration_cling.xml
index db93da8..269c1ae 100644
--- a/res/layout-land/migration_cling.xml
+++ b/res/layout-land/migration_cling.xml
@@ -46,6 +46,7 @@
         android:layout_width="@dimen/cling_migration_content_width"
         android:layout_height="wrap_content"
         android:layout_marginEnd="@dimen/cling_migration_content_margin"
+        android:layout_marginRight="@dimen/cling_migration_content_margin"
         android:orientation="vertical"
         android:paddingLeft="24dp"
         android:paddingRight="24dp" >
diff --git a/res/layout-port/migration_cling.xml b/res/layout-port/migration_cling.xml
index 81689d3..3f696a2 100644
--- a/res/layout-port/migration_cling.xml
+++ b/res/layout-port/migration_cling.xml
@@ -48,6 +48,7 @@
             android:layout_height="wrap_content"
             android:layout_below="@+id/ic_cling_migration"
             android:layout_marginStart="@dimen/cling_migration_content_margin"
+            android:layout_marginLeft="@dimen/cling_migration_content_margin"
             android:orientation="vertical"
             android:paddingLeft="24dp"
             android:paddingRight="24dp" >
diff --git a/res/layout/apps_grid_icon_view.xml b/res/layout/apps_grid_icon_view.xml
index 67d7d50..7165f38 100644
--- a/res/layout/apps_grid_icon_view.xml
+++ b/res/layout/apps_grid_icon_view.xml
@@ -25,6 +25,5 @@
     android:paddingBottom="@dimen/apps_icon_top_bottom_padding"
     android:focusable="true"
     android:background="@drawable/focusable_view_bg"
-    launcher:deferShadowGeneration="true"
     launcher:iconDisplay="all_apps" />
 
diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml
index ef20323..6f9be05 100644
--- a/res/layout/apps_list_view.xml
+++ b/res/layout/apps_list_view.xml
@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!--
+     Copyright (C) 2015 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,82 +14,92 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout
+<com.android.launcher3.AppsRecyclerViewContainer
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/apps_list"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:elevation="15dp"
-    android:visibility="gone"
-    android:focusableInTouchMode="true">
+    android:focusableInTouchMode="true"
+    android:visibility="gone" >
+
     <com.android.launcher3.AppsContainerRecyclerView
         android:id="@+id/apps_list_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginTop="@dimen/apps_search_bar_height"
         android:layout_gravity="center_horizontal|top"
+        android:layout_marginTop="@dimen/apps_search_bar_height"
         android:clipToPadding="false"
-        android:focusable="true"
-        android:descendantFocusability="afterDescendants" />
+        android:descendantFocusability="afterDescendants"
+        android:focusable="true" />
+
     <LinearLayout
         android:id="@+id/prediction_bar"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/apps_search_bar_height"
         android:orientation="horizontal"
-        android:visibility="invisible">
+        android:visibility="invisible" >
     </LinearLayout>
 
     <!-- We always want the search bar on top, so it goes last. -->
+
     <FrameLayout
         android:id="@+id/header"
         android:layout_width="match_parent"
         android:layout_height="@dimen/apps_search_bar_height"
-        android:background="@drawable/apps_search_bg">
+        android:background="@drawable/apps_search_bg" >
+
         <LinearLayout
             android:id="@+id/app_search_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="horizontal"
-            android:visibility="invisible">
+            android:visibility="invisible" >
+
             <ImageView
                 android:id="@+id/dismiss_search_button"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="start|center_vertical"
+                android:layout_marginLeft="4dp"
                 android:layout_marginStart="4dp"
-                android:paddingTop="13dp"
-                android:paddingBottom="13dp"
                 android:contentDescription="@string/all_apps_button_label"
+                android:paddingBottom="13dp"
+                android:paddingTop="13dp"
                 android:src="@drawable/ic_arrow_back_grey" />
+
             <com.android.launcher3.AppsContainerSearchEditTextView
                 android:id="@+id/app_search_box"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:paddingTop="16dp"
+                android:background="@android:color/transparent"
+                android:focusableInTouchMode="true"
+                android:gravity="fill_horizontal"
+                android:hint="@string/apps_view_search_bar_hint"
+                android:imeOptions="actionDone|flagNoExtractUi"
+                android:maxLines="1"
                 android:paddingBottom="16dp"
                 android:paddingLeft="8dp"
-                android:hint="@string/apps_view_search_bar_hint"
-                android:maxLines="1"
-                android:singleLine="true"
+                android:paddingTop="16dp"
                 android:scrollHorizontally="true"
-                android:gravity="fill_horizontal"
-                android:textSize="16sp"
+                android:singleLine="true"
                 android:textColor="#4c4c4c"
                 android:textColorHint="#9c9c9c"
-                android:imeOptions="actionDone|flagNoExtractUi"
-                android:focusableInTouchMode="true"
-                android:background="@android:color/transparent" />
+                android:textSize="16sp" />
         </LinearLayout>
+
         <ImageView
             android:id="@+id/search_button"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="end|center_vertical"
             android:layout_marginEnd="6dp"
-            android:paddingTop="13dp"
-            android:paddingBottom="13dp"
+            android:layout_marginRight="6dp"
             android:contentDescription="@string/apps_view_search_bar_hint"
+            android:paddingBottom="13dp"
+            android:paddingTop="13dp"
             android:src="@drawable/ic_search_grey" />
     </FrameLayout>
-</FrameLayout>
\ No newline at end of file
+
+</com.android.launcher3.AppsRecyclerViewContainer>
\ No newline at end of file
diff --git a/res/layout/apps_prediction_bar_icon_view.xml b/res/layout/apps_prediction_bar_icon_view.xml
index 4a6f157..1e75d14 100644
--- a/res/layout/apps_prediction_bar_icon_view.xml
+++ b/res/layout/apps_prediction_bar_icon_view.xml
@@ -24,6 +24,5 @@
     android:layout_weight="1"
     android:focusable="true"
     android:background="@drawable/focusable_view_bg"
-    launcher:deferShadowGeneration="true"
     launcher:iconDisplay="all_apps" />
 
diff --git a/res/layout/appwidget_error.xml b/res/layout/appwidget_error.xml
index 03d4ae4..708ece4 100644
--- a/res/layout/appwidget_error.xml
+++ b/res/layout/appwidget_error.xml
@@ -15,15 +15,12 @@
 -->
 
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:paddingTop="10dip"
-    android:paddingBottom="10dip"
-    android:paddingLeft="20dip"
-    android:paddingRight="20dip"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
     android:gravity="center"
-    android:background="@drawable/bg_appwidget_error"
+    android:elevation="2dp"
+    android:background="@drawable/quantum_panel_dark"
     android:textAppearance="?android:attr/textAppearanceMediumInverse"
-    android:textColor="@color/appwidget_error_color"
+    android:textColor="@color/widgets_view_item_text_color"
     android:text="@string/gadget_error_text"
     />
diff --git a/res/layout/search_drop_target_bar.xml b/res/layout/search_drop_target_bar.xml
index 69f42bb..b0435aa 100644
--- a/res/layout/search_drop_target_bar.xml
+++ b/res/layout/search_drop_target_bar.xml
@@ -36,6 +36,7 @@
             <com.android.launcher3.DeleteDropTarget
                 android:id="@+id/delete_target_text"
                 style="@style/DropTargetButton"
+                android:drawableLeft="@drawable/remove_target_selector"
                 android:drawableStart="@drawable/remove_target_selector"
                 android:text="@string/delete_target_label" />
         </FrameLayout>
@@ -49,6 +50,7 @@
             <com.android.launcher3.InfoDropTarget
                 android:id="@+id/info_target_text"
                 style="@style/DropTargetButton"
+                android:drawableLeft="@drawable/info_target_selector"
                 android:drawableStart="@drawable/info_target_selector"
                 android:text="@string/info_target_label" />
         </FrameLayout>
@@ -62,6 +64,7 @@
             <com.android.launcher3.UninstallDropTarget
                 android:id="@+id/uninstall_target_text"
                 style="@style/DropTargetButton"
+                android:drawableLeft="@drawable/uninstall_target_selector"
                 android:drawableStart="@drawable/uninstall_target_selector"
                 android:text="@string/delete_target_uninstall_label" />
         </FrameLayout>
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index d75d6cd..67b69ca 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -38,6 +38,9 @@
             android:id="@+id/folder_content"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
+            android:paddingLeft="4dp"
+            android:paddingRight="4dp"
+            android:paddingTop="4dp"
             launcher:pageIndicator="@+id/folder_page_indicator" />
     </FrameLayout>
 
diff --git a/res/layout/widget_cell.xml b/res/layout/widget_cell.xml
index a85f0aa..7fefeba 100644
--- a/res/layout/widget_cell.xml
+++ b/res/layout/widget_cell.xml
@@ -39,16 +39,15 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_weight="1"
-            android:gravity="start"
-            android:singleLine="true"
             android:ellipsize="end"
             android:fadingEdge="horizontal"
-            android:textColor="@color/widgets_view_item_text_color"
-            android:textSize="16sp"
-            android:textAlignment="viewStart"
             android:fontFamily="sans-serif-condensed"
+            android:gravity="start"
+            android:shadowColor="#B0000000"
             android:shadowRadius="2.0"
-            android:shadowColor="#B0000000" />
+            android:singleLine="true"
+            android:textColor="@color/widgets_view_item_text_color"
+            android:textSize="14sp" />
 
         <!-- The original dimensions of the widget (can't be the same text as above due to different
              style. -->
@@ -58,22 +57,18 @@
             android:layout_height="wrap_content"
             android:layout_marginStart="5dp"
             android:layout_marginLeft="5dp"
-            android:layout_weight="0"
-            android:gravity="start"
             android:textColor="@color/widgets_view_item_text_color"
-            android:textSize="16sp"
-            android:textAlignment="viewStart"
+            android:textSize="14sp"
             android:fontFamily="sans-serif-condensed"
             android:shadowRadius="2.0"
             android:shadowColor="#B0000000" />
     </LinearLayout>
 
-    <!-- The image of the widget. -->
+    <!-- The image of the widget. This view does not support padding. Any placement adjustment
+         should be done using margins. -->
     <com.android.launcher3.widget.WidgetImageView
         android:id="@+id/widget_preview"
-        style="@style/WidgetImageView"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:scaleType="matrix" />
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
 </com.android.launcher3.widget.WidgetCell>
\ No newline at end of file
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index 2cbdb5c..dc1bcce 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -41,7 +41,7 @@
         android:paddingTop="@dimen/widget_section_vertical_padding"
         android:singleLine="true"
         android:textColor="@color/widgets_view_section_text_color"
-        android:textSize="20sp"
+        android:textSize="16sp"
         launcher:customShadows="false"
         launcher:deferShadowGeneration="true"
         launcher:iconDisplay="widget_section"
@@ -58,6 +58,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_marginStart="@dimen/widget_row_padding"
+            android:layout_marginLeft="@dimen/widget_row_padding"
             android:orientation="horizontal"
             android:divider="@drawable/widgets_row_divider"
             android:showDividers="middle"
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index b91ca26..1857595 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -40,6 +40,7 @@
         android:layout_height="match_parent"
         android:clipChildren="false"
         android:elevation="15dp"
+        android:visibility="gone"
         android:orientation="vertical" >
 
         <com.android.launcher3.widget.WidgetsContainerRecyclerView
diff --git a/res/values-land/styles.xml b/res/values-land/styles.xml
index 8a255c9..c5a76d5 100644
--- a/res/values-land/styles.xml
+++ b/res/values-land/styles.xml
@@ -28,7 +28,7 @@
     </style>
 
     <!-- This style applies to the drop target when it is shown in the sidebar -->
-    <style name="DropTargetButton" parent="DropTargetButton.Base">
+    <style name="DropTargetButton" parent="DropTargetButtonBase">
         <item name="android:layout_height">wrap_content</item>
         <item name="android:gravity">center</item>
         <item name="android:drawablePadding">0dp</item>
diff --git a/res/values-sw720dp/styles.xml b/res/values-sw720dp/styles.xml
index cbc1e29..e8b706e 100644
--- a/res/values-sw720dp/styles.xml
+++ b/res/values-sw720dp/styles.xml
@@ -27,7 +27,7 @@
         <item name="android:layout_height">match_parent</item>
     </style>
 
-    <style name="DropTargetButton" parent="DropTargetButton.Base">
+    <style name="DropTargetButton" parent="DropTargetButtonBase">
         <item name="android:paddingLeft">60dp</item>
         <item name="android:paddingRight">60dp</item>
         <item name="android:shadowColor">#393939</item>
diff --git a/res/values-v17/styles.xml b/res/values-v17/styles.xml
deleted file mode 100644
index 3589e80..0000000
--- a/res/values-v17/styles.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <style name="WidgetImageView">
-        <item name="android:paddingStart">@dimen/widget_preview_horizontal_padding</item>
-    </style>
-</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 0ba55f3..1e89615 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -27,8 +27,6 @@
 
     <color name="focused_background">#80c6c5c5</color>
 
-    <color name="appwidget_error_color">#FCCC</color>
-
     <color name="workspace_icon_text_color">#FFF</color>
 
     <color name="quantum_panel_text_color">#FF666666</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 2fa16e7..60591e1 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -28,7 +28,10 @@
     <dimen name="dynamic_grid_overview_bar_item_width">80dp</dimen>
     <dimen name="dynamic_grid_overview_bar_spacer_width">20dp</dimen>
 
+<!-- App Widget resize frame -->
     <dimen name="default_widget_padding">8dp</dimen>
+    <dimen name="widget_handle_margin">13dp</dimen>
+    <dimen name="resize_frame_background_padding">24dp</dimen>
 
 <!-- Cling -->
     <dimen name="cling_migration_logo_height">240dp</dimen>
@@ -71,6 +74,10 @@
     <dimen name="drop_target_drag_padding">14dp</dimen>
     <dimen name="drop_target_text_size">14sp</dimen>
 
+    <dimen name="all_apps_header_max_elevation">4dp</dimen>
+    <dimen name="all_apps_header_scroll_to_elevation">16dp</dimen>
+    <dimen name="all_apps_header_shadow_height">6dp</dimen>
+
 <!-- Dragging -->
     <!-- the area at the edge of the screen that makes the workspace go left
          or right while you're dragging. -->
@@ -114,4 +121,12 @@
 <!-- Sizes for managed profile badges -->
     <dimen name="profile_badge_size">24dp</dimen>
     <dimen name="profile_badge_margin">4dp</dimen>
+    <dimen name="profile_badge_minimum_top">2dp</dimen>
+
+<!-- Shadows and outlines -->
+    <dimen name="blur_size_thin_outline">1dp</dimen>
+    <dimen name="blur_size_medium_outline">2dp</dimen>
+    <dimen name="blur_size_click_shadow">4dp</dimen>
+    <dimen name="click_shadow_high_shift">2dp</dimen>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 51ad51f..18f97c8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -182,76 +182,76 @@
     </string>
 
 <!-- Strings for accessibility actions -->
-    <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] [DO NOT TRANSLATE] -->
-    <string name="action_add_to_workspace">Add to home screen</string>
+    <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
+    <string name="action_add_to_workspace">Add to Home screen</string>
 
-    <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] [DO NOT TRANSLATE] -->
-    <string name="action_move_here">Move here</string>
+    <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
+    <string name="action_move_here">Move item here</string>
 
-    <!-- Accessibility confirmation for item added to workspace [DO NOT TRANSLATE] -->
+    <!-- Accessibility confirmation for item added to workspace. -->
     <string name="item_added_to_workspace">Item added to home screen</string>
 
-    <!-- Accessibility confirmation for item removed [DO NOT TRANSLATE] -->
+    <!-- Accessibility confirmation for item removed. -->
     <string name="item_removed">Item removed</string>
 
-    <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] [DO NOT TRANSLATE] -->
-    <string name="action_move">Move Item</string>
+    <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
+    <string name="action_move">Move item</string>
 
-    <!-- Accessibility description to move item to empty cell. [DO NOT TRANSLATE] -->
+    <!-- Accessibility description to move item to empty cell. -->
     <string name="move_to_empty_cell">Move to row <xliff:g id="number" example="1">%1$s</xliff:g> column <xliff:g id="number" example="1">%2$s</xliff:g></string>
 
-    <!-- Accessibility description to move item inside a folder. [DO NOT TRANSLATE] -->
+    <!-- Accessibility description to move item inside a folder. -->
     <string name="move_to_position">Move to position <xliff:g id="number" example="1">%1$s</xliff:g></string>
 
-    <!-- Accessibility description to move item to the hotseat. [DO NOT TRANSLATE] -->
+    <!-- Accessibility description to move item to the hotseat. -->
     <string name="move_to_hotseat_position">Move to favorites position <xliff:g id="number" example="1">%1$s</xliff:g></string>
 
-    <!-- Accessibility confirmation for item move [DO NOT TRANSLATE]-->
+    <!-- Accessibility confirmation for item move. -->
     <string name="item_moved">Item moved</string>
 
-    <!-- Accessibility description to move item into an existing folder. [DO NOT TRANSLATE]-->
+    <!-- Accessibility description to move item into an existing folder. -->
     <string name="add_to_folder">Add to folder: <xliff:g id="name" example="Games">%1$s</xliff:g></string>
 
-    <!-- Accessibility description to move item into an existing folder containing an app. [DO NOT TRANSLATE]-->
+    <!-- Accessibility description to move item into an existing folder containing an app. -->
     <string name="add_to_folder_with_app">Add to folder with <xliff:g id="name" example="Messenger">%1$s</xliff:g></string>
 
-    <!-- Accessibility confirmation for item added to folder [DO NOT TRANSLATE] -->
+    <!-- Accessibility confirmation for item added to folder. -->
     <string name="added_to_folder">Item added to folder</string>
 
-    <!-- Accessibility description to create folder with another item. [DO NOT TRANSLATE] -->
+    <!-- Accessibility description to create folder with another item. -->
     <string name="create_folder_with">Create folder with: <xliff:g id="name" example="Game">%1$s</xliff:g></string>
 
-    <!-- Accessibility confirmation for folder created [DO NOT TRANSLATE] -->
+    <!-- Accessibility confirmation for folder created. -->
     <string name="folder_created">Folder created</string>
 
-    <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] [DO NOT TRANSLATE] -->
-    <string name="action_move_to_workspace">Move to home screen</string>
+    <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
+    <string name="action_move_to_workspace">Move to Home screen</string>
 
-    <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] [DO NOT TRANSLATE] -->
+    <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
     <string name="action_move_screen_left">Move screen to left</string>
 
-    <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] [DO NOT TRANSLATE] -->
+    <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
     <string name="action_move_screen_right">Move screen to right</string>
 
-    <!-- Accessibility confirmation when a screen was moved [DO NOT TRANSLATE] -->
+    <!-- Accessibility confirmation when a screen was moved. -->
     <string name="screen_moved">Screen moved</string>
 
-    <!-- Accessibility action to resize a widget [DO NOT TRANSLATE] -->
+    <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
     <string name="action_resize">Resize</string>
 
-    <!-- Accessibility action to increase width of a widget [DO NOT TRANSLATE] -->
+    <!-- Accessibility action to increase width of a widget. [CHAR_LIMIT=30] -->
     <string name="action_increase_width">Increase width</string>
 
-    <!-- Accessibility action to increase height of a widget [DO NOT TRANSLATE] -->
+    <!-- Accessibility action to increase height of a widget. [CHAR_LIMIT=30] -->
     <string name="action_increase_height">Increase height</string>
 
-    <!-- Accessibility action to decrease width of a widget [DO NOT TRANSLATE] -->
+    <!-- Accessibility action to decrease width of a widget. [CHAR_LIMIT=30] -->
     <string name="action_decrease_width">Decrease width</string>
 
-    <!-- Accessibility action to decrease height of a widget [DO NOT TRANSLATE] -->
+    <!-- Accessibility action to decrease height of a widget. [CHAR_LIMIT=30] -->
     <string name="action_decrease_height">Decrease height</string>
 
-    <!-- Accessibility confirmation for widget resize [DO NOT TRANSLATE]-->
+    <!-- Accessibility confirmation for widget resize. -->
     <string name="widget_resized">Widget resized to width <xliff:g id="number" example="2">%1$s</xliff:g> height <xliff:g id="number" example="1">%2$s</xliff:g></string>
 
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 78cc083..1496da9 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -54,7 +54,7 @@
         <item name="android:layout_height">match_parent</item>
     </style>
 
-    <style name="DropTargetButton.Base">
+    <style name="DropTargetButtonBase">
         <item name="android:layout_width">wrap_content</item>
         <item name="android:layout_height">match_parent</item>
         <item name="android:layout_gravity">center</item>
@@ -72,7 +72,7 @@
         <item name="android:shadowRadius">4.0</item>
     </style>
 
-    <style name="DropTargetButton" parent="DropTargetButton.Base"></style>
+    <style name="DropTargetButton" parent="DropTargetButtonBase" />
 
     <style name="PreloadIcon">
         <item name="background">@drawable/virtual_preload</item>
@@ -86,9 +86,4 @@
         <item name="ringOutset">4dp</item>
     </style>
 
-    <!-- Overridden in device overlays -->
-    <style name="WidgetImageView">
-        <item name="android:paddingLeft">@dimen/widget_preview_horizontal_padding</item>
-    </style>
-
 </resources>
diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java
index eff7b06..82aaeb9 100644
--- a/src/com/android/launcher3/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/AlphabeticalAppsList.java
@@ -7,6 +7,7 @@
 import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.model.AppNameComparator;
 
 import java.text.Collator;
 import java.util.ArrayList;
@@ -18,96 +19,6 @@
 import java.util.Map;
 import java.util.TreeMap;
 
-
-/**
- * A private class to manage access to an app name comparator.
- */
-class AppNameComparator {
-    private final UserManagerCompat mUserManager;
-    private final Collator mCollator;
-    private final Comparator<AppInfo> mAppInfoComparator;
-    private final Comparator<String> mSectionNameComparator;
-    private HashMap<UserHandleCompat, Long> mUserSerialCache = new HashMap<>();
-
-    public AppNameComparator(Context context) {
-        mCollator = Collator.getInstance();
-        mUserManager = UserManagerCompat.getInstance(context);
-        mAppInfoComparator = new Comparator<AppInfo>() {
-            public final int compare(AppInfo a, AppInfo b) {
-                // Order by the title in the current locale
-                int result = compareTitles(a.title.toString(), b.title.toString());
-                if (result == 0) {
-                    // If two apps have the same title, then order by the component name
-                    result = a.componentName.compareTo(b.componentName);
-                    if (result == 0) {
-                        // If the two apps are the same component, then prioritize by the order that
-                        // the app user was created (prioritizing the main user's apps)
-                        if (UserHandleCompat.myUserHandle().equals(a.user)) {
-                            return -1;
-                        } else {
-                            Long aUserSerial = getAndCacheUserSerial(a.user);
-                            Long bUserSerial = getAndCacheUserSerial(b.user);
-                            return aUserSerial.compareTo(bUserSerial);
-                        }
-                    }
-                }
-                return result;
-            }
-        };
-        mSectionNameComparator = new Comparator<String>() {
-            @Override
-            public int compare(String o1, String o2) {
-                return compareTitles(o1, o2);
-            }
-        };
-    }
-
-    /**
-     * Returns a locale-aware comparator that will alphabetically order a list of applications.
-     */
-    public Comparator<AppInfo> getAppInfoComparator() {
-        // Clear the user serial cache so that we get serials as needed in the comparator
-        mUserSerialCache.clear();
-        return mAppInfoComparator;
-    }
-
-    /**
-     * Returns a locale-aware comparator that will alphabetically order a list of section names.
-     */
-    public Comparator<String> getSectionNameComparator() {
-        return mSectionNameComparator;
-    }
-
-    /**
-     * Compares two titles with the same return value semantics as Comparator.
-     */
-    private int compareTitles(String titleA, String titleB) {
-        // Ensure that we de-prioritize any titles that don't start with a linguistic letter or digit
-        boolean aStartsWithLetter = Character.isLetterOrDigit(titleA.codePointAt(0));
-        boolean bStartsWithLetter = Character.isLetterOrDigit(titleB.codePointAt(0));
-        if (aStartsWithLetter && !bStartsWithLetter) {
-            return -1;
-        } else if (!aStartsWithLetter && bStartsWithLetter) {
-            return 1;
-        }
-
-        // Order by the title in the current locale
-        return mCollator.compare(titleA, titleB);
-    }
-
-    /**
-     * Returns the user serial for this user, using a cached serial if possible.
-     */
-    private Long getAndCacheUserSerial(UserHandleCompat user) {
-        Long userSerial = mUserSerialCache.get(user);
-        if (userSerial == null) {
-            userSerial = mUserManager.getSerialNumberForUser(user);
-            mUserSerialCache.put(user, userSerial);
-        }
-        return userSerial;
-    }
-}
-
 /**
  * The alphabetically sorted list of applications.
  */
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 3c698c0..e6bf525 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -15,24 +15,36 @@
 import android.widget.ImageView;
 
 public class AppWidgetResizeFrame extends FrameLayout {
-    private LauncherAppWidgetHostView mWidgetView;
-    private CellLayout mCellLayout;
-    private DragLayer mDragLayer;
-    private ImageView mLeftHandle;
-    private ImageView mRightHandle;
-    private ImageView mTopHandle;
-    private ImageView mBottomHandle;
+    private static final int SNAP_DURATION = 150;
+    private static final float DIMMED_HANDLE_ALPHA = 0f;
+    private static final float RESIZE_THRESHOLD = 0.66f;
+
+    private static Rect sTmpRect = new Rect();
+
+    private final Launcher mLauncher;
+    private final LauncherAppWidgetHostView mWidgetView;
+    private final CellLayout mCellLayout;
+    private final DragLayer mDragLayer;
+
+    private final ImageView mLeftHandle;
+    private final ImageView mRightHandle;
+    private final ImageView mTopHandle;
+    private final ImageView mBottomHandle;
+
+    private final Rect mWidgetPadding;
+
+    private final int mBackgroundPadding;
+    private final int mTouchTargetWidth;
+
+    private final int[] mDirectionVector = new int[2];
+    private final int[] mLastDirectionVector = new int[2];
+    private final int[] mTmpPt = new int[2];
 
     private boolean mLeftBorderActive;
     private boolean mRightBorderActive;
     private boolean mTopBorderActive;
     private boolean mBottomBorderActive;
 
-    private int mWidgetPaddingLeft;
-    private int mWidgetPaddingRight;
-    private int mWidgetPaddingTop;
-    private int mWidgetPaddingBottom;
-
     private int mBaselineWidth;
     private int mBaselineHeight;
     private int mBaselineX;
@@ -48,30 +60,9 @@
     private int mDeltaXAddOn;
     private int mDeltaYAddOn;
 
-    private int mBackgroundPadding;
-    private int mTouchTargetWidth;
-
     private int mTopTouchRegionAdjustment = 0;
     private int mBottomTouchRegionAdjustment = 0;
 
-    int[] mDirectionVector = new int[2];
-    int[] mLastDirectionVector = new int[2];
-    int[] mTmpPt = new int[2];
-
-    final int SNAP_DURATION = 150;
-    final int BACKGROUND_PADDING = 24;
-    final float DIMMED_HANDLE_ALPHA = 0f;
-    final float RESIZE_THRESHOLD = 0.66f;
-
-    private static Rect mTmpRect = new Rect();
-
-    public static final int LEFT = 0;
-    public static final int TOP = 1;
-    public static final int RIGHT = 2;
-    public static final int BOTTOM = 3;
-
-    private Launcher mLauncher;
-
     public AppWidgetResizeFrame(Context context,
             LauncherAppWidgetHostView widgetView, CellLayout cellLayout, DragLayer dragLayer) {
 
@@ -87,49 +78,49 @@
         mMinHSpan = info.minSpanX;
         mMinVSpan = info.minSpanY;
 
-        setBackgroundResource(R.drawable.widget_resize_frame_holo);
+        setBackgroundResource(R.drawable.widget_resize_shadow);
+        setForeground(getResources().getDrawable(R.drawable.widget_resize_frame));
         setPadding(0, 0, 0, 0);
 
+        final int handleMargin = getResources().getDimensionPixelSize(R.dimen.widget_handle_margin);
         LayoutParams lp;
         mLeftHandle = new ImageView(context);
-        mLeftHandle.setImageResource(R.drawable.widget_resize_handle_left);
-        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 
+        mLeftHandle.setImageResource(R.drawable.ic_widget_resize_handle);
+        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
                 Gravity.LEFT | Gravity.CENTER_VERTICAL);
+        lp.leftMargin = handleMargin;
         addView(mLeftHandle, lp);
 
         mRightHandle = new ImageView(context);
-        mRightHandle.setImageResource(R.drawable.widget_resize_handle_right);
-        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 
+        mRightHandle.setImageResource(R.drawable.ic_widget_resize_handle);
+        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
                 Gravity.RIGHT | Gravity.CENTER_VERTICAL);
+        lp.rightMargin = handleMargin;
         addView(mRightHandle, lp);
 
         mTopHandle = new ImageView(context);
-        mTopHandle.setImageResource(R.drawable.widget_resize_handle_top);
-        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 
+        mTopHandle.setImageResource(R.drawable.ic_widget_resize_handle);
+        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
                 Gravity.CENTER_HORIZONTAL | Gravity.TOP);
+        lp.topMargin = handleMargin;
         addView(mTopHandle, lp);
 
         mBottomHandle = new ImageView(context);
-        mBottomHandle.setImageResource(R.drawable.widget_resize_handle_bottom);
-        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 
+        mBottomHandle.setImageResource(R.drawable.ic_widget_resize_handle);
+        lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,
                 Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
+        lp.bottomMargin = handleMargin;
         addView(mBottomHandle, lp);
 
-        Rect p = new Rect(0, 0, 0, 0);
         if (!info.isCustomWidget) {
-            p = AppWidgetHostView.getDefaultPaddingForWidget(context,
+            mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context,
                     widgetView.getAppWidgetInfo().provider, null);
         } else {
             Resources r = context.getResources();
             int padding = r.getDimensionPixelSize(R.dimen.default_widget_padding);
-            p.set(padding, padding, padding, padding);
+            mWidgetPadding = new Rect(padding, padding, padding, padding);
         }
 
-        mWidgetPaddingLeft = p.left;
-        mWidgetPaddingTop = p.top;
-        mWidgetPaddingRight = p.right;
-        mWidgetPaddingBottom = p.bottom;
-
         if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
             mTopHandle.setVisibility(GONE);
             mBottomHandle.setVisibility(GONE);
@@ -138,8 +129,8 @@
             mRightHandle.setVisibility(GONE);
         }
 
-        final float density = mLauncher.getResources().getDisplayMetrics().density;
-        mBackgroundPadding = (int) Math.ceil(density * BACKGROUND_PADDING);
+        mBackgroundPadding = getResources()
+                .getDimensionPixelSize(R.dimen.resize_frame_background_padding);
         mTouchTargetWidth = 2 * mBackgroundPadding;
 
         // When we create the resize frame, we first mark all cells as unoccupied. The appropriate
@@ -344,9 +335,9 @@
 
     static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher,
             int spanX, int spanY) {
-        getWidgetSizeRanges(launcher, spanX, spanY, mTmpRect);
-        widgetView.updateAppWidgetSize(null, mTmpRect.left, mTmpRect.top,
-                mTmpRect.right, mTmpRect.bottom);
+        getWidgetSizeRanges(launcher, spanX, spanY, sTmpRect);
+        widgetView.updateAppWidgetSize(null, sTmpRect.left, sTmpRect.top,
+                sTmpRect.right, sTmpRect.bottom);
     }
 
     public static Rect getWidgetSizeRanges(Launcher launcher, int spanX, int spanY, Rect rect) {
@@ -404,19 +395,19 @@
 
     public void snapToWidget(boolean animate) {
         final DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
-        int newWidth = mWidgetView.getWidth() + 2 * mBackgroundPadding - mWidgetPaddingLeft -
-                mWidgetPaddingRight;
-        int newHeight = mWidgetView.getHeight() + 2 * mBackgroundPadding - mWidgetPaddingTop -
-                mWidgetPaddingBottom;
+        int newWidth = mWidgetView.getWidth() + 2 * mBackgroundPadding
+                - mWidgetPadding.left - mWidgetPadding.right;
+        int newHeight = mWidgetView.getHeight() + 2 * mBackgroundPadding
+                - mWidgetPadding.top - mWidgetPadding.bottom;
 
         mTmpPt[0] = mWidgetView.getLeft();
         mTmpPt[1] = mWidgetView.getTop();
         mDragLayer.getDescendantCoordRelativeToSelf(mCellLayout.getShortcutsAndWidgets(), mTmpPt);
 
-        int newX = mTmpPt[0] - mBackgroundPadding + mWidgetPaddingLeft;
-        int newY = mTmpPt[1] - mBackgroundPadding + mWidgetPaddingTop;
+        int newX = mTmpPt[0] - mBackgroundPadding + mWidgetPadding.left;
+        int newY = mTmpPt[1] - mBackgroundPadding + mWidgetPadding.top;
 
-        // We need to make sure the frame's touchable regions lie fully within the bounds of the 
+        // We need to make sure the frame's touchable regions lie fully within the bounds of the
         // DragLayer. We allow the actual handles to be clipped, but we shift the touch regions
         // down accordingly to provide a proper touch target.
         if (newY < 0) {
diff --git a/src/com/android/launcher3/AppsContainerRecyclerView.java b/src/com/android/launcher3/AppsContainerRecyclerView.java
index 3859be8..31942b3 100644
--- a/src/com/android/launcher3/AppsContainerRecyclerView.java
+++ b/src/com/android/launcher3/AppsContainerRecyclerView.java
@@ -61,6 +61,8 @@
 
     private Drawable mScrollbar;
     private Drawable mFastScrollerBg;
+    private Rect mTmpFastScrollerInvalidateRect = new Rect();
+    private Rect mFastScrollerBounds = new Rect();
     private Rect mVerticalScrollbarBounds = new Rect();
     private boolean mDraggingFastScroller;
     private String mFastScrollSectionName;
@@ -120,35 +122,23 @@
         mApps = apps;
     }
 
-    @Override
-    public void setAdapter(Adapter adapter) {
-        // Register a change listener to update the scroll position state whenever the data set
-        // changes.
-        adapter.registerAdapterDataObserver(new AdapterDataObserver() {
-            @Override
-            public void onChanged() {
-                post(new Runnable() {
-                    @Override
-                    public void run() {
-                        refreshCurScrollPosition();
-                    }
-                });
-            }
-        });
-        super.setAdapter(adapter);
-    }
-
     /**
      * Sets the number of apps per row in this recycler view.
      */
     public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow) {
         mNumAppsPerRow = numAppsPerRow;
         mNumPredictedAppsPerRow = numPredictedAppsPerRow;
+
+        DeviceProfile grid = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+        RecyclerView.RecycledViewPool pool = getRecycledViewPool();
+        int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
+        pool.setMaxRecycledViews(AppsGridAdapter.PREDICTION_BAR_SPACER_TYPE, 1);
+        pool.setMaxRecycledViews(AppsGridAdapter.EMPTY_VIEW_TYPE, 1);
+        pool.setMaxRecycledViews(AppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow);
+        pool.setMaxRecycledViews(AppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows);
     }
 
-    @Override
-    public void setBackground(Drawable background) {
-        super.setBackground(background);
+    public void updateBackgroundPadding(Drawable background) {
         background.getPadding(mBackgroundPadding);
     }
 
@@ -164,7 +154,7 @@
      */
     public void setFastScrollerAlpha(float alpha) {
         mFastScrollAlpha = alpha;
-        invalidateFastScroller();
+        invalidateFastScroller(mFastScrollerBounds);
     }
 
     /**
@@ -186,7 +176,20 @@
      */
     public void scrollToTop() {
         scrollToPosition(0);
-        updateScrollY(0);
+    }
+
+    /**
+     * Returns the current scroll position.
+     */
+    public int getScrollPosition() {
+        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+        getCurScrollState(mScrollPosState, items);
+        if (mScrollPosState.rowIndex != -1) {
+            int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight;
+            return getPaddingTop() + (mScrollPosState.rowIndex * mScrollPosState.rowHeight) +
+                    predictionBarHeight - mScrollPosState.rowTopOffset;
+        }
+        return 0;
     }
 
     @Override
@@ -254,7 +257,13 @@
                     float boundedY = (float) Math.max(top, Math.min(bottom, y));
                     mFastScrollSectionName = scrollToPositionAtProgress((boundedY - top) /
                             (bottom - top));
-                    invalidateFastScroller();
+
+                    // Combine the old and new fast scroller bounds to create the full invalidate
+                    // rect
+                    mTmpFastScrollerInvalidateRect.set(mFastScrollerBounds);
+                    updateFastScrollerBounds();
+                    mTmpFastScrollerInvalidateRect.union(mFastScrollerBounds);
+                    invalidateFastScroller(mTmpFastScrollerInvalidateRect);
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -290,25 +299,9 @@
      */
     private void drawFastScrollerPopup(Canvas canvas) {
         if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) {
-            int x;
-            int y;
-            boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
-                    LAYOUT_DIRECTION_RTL);
-
-            // Calculate the position for the fast scroller popup
-            Rect bgBounds = mFastScrollerBg.getBounds();
-            if (isRtl) {
-                x = mBackgroundPadding.left + getScrollBarSize();
-            } else {
-                x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width();
-            }
-            y = mLastY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgBounds.height());
-            y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() -
-                    bgBounds.height()));
-
             // Draw the fast scroller popup
             int restoreCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
-            canvas.translate(x, y);
+            canvas.translate(mFastScrollerBounds.left, mFastScrollerBounds.top);
             mFastScrollerBg.setAlpha((int) (mFastScrollAlpha * 255));
             mFastScrollerBg.draw(canvas);
             mFastScrollTextPaint.setAlpha((int) (mFastScrollAlpha * 255));
@@ -316,8 +309,9 @@
                     mFastScrollSectionName.length(), mFastScrollTextBounds);
             float textWidth = mFastScrollTextPaint.measureText(mFastScrollSectionName);
             canvas.drawText(mFastScrollSectionName,
-                    (bgBounds.width() - textWidth) / 2,
-                    bgBounds.height() - (bgBounds.height() - mFastScrollTextBounds.height()) / 2,
+                    (mFastScrollerBounds.width() - textWidth) / 2,
+                    mFastScrollerBounds.height() -
+                            (mFastScrollerBounds.height() - mFastScrollTextBounds.height()) / 2,
                     mFastScrollTextPaint);
             canvas.restoreToCount(restoreCount);
         }
@@ -340,9 +334,8 @@
     /**
      * Invalidates the fast scroller popup.
      */
-    private void invalidateFastScroller() {
-        invalidate(getWidth() - mBackgroundPadding.right - getScrollBarSize() -
-                mFastScrollerBg.getIntrinsicWidth(), 0, getWidth(), getHeight());
+    private void invalidateFastScroller(Rect bounds) {
+        invalidate(bounds.left, bounds.top, bounds.right, bounds.bottom);
     }
 
     /**
@@ -362,12 +355,11 @@
 
         // If there is a prediction bar, then capture the appropriate area for the prediction bar
         float predictionBarFraction = 0f;
-        if (mPredictionBarHeight > 0) {
+        if (!mApps.getPredictedApps().isEmpty()) {
             predictionBarFraction = (float) mNumPredictedAppsPerRow / mApps.getSize();
             if (touchFraction <= predictionBarFraction) {
                 // Scroll to the top of the view, where the prediction bar is
                 layoutManager.scrollToPositionWithOffset(0, 0);
-                updateScrollY(0);
                 return "";
             }
         }
@@ -386,9 +378,6 @@
             lastScrollSection = scrollSection;
         }
 
-        // We need to workaround the RecyclerView to get the right scroll position
-        refreshCurScrollPosition();
-
         // Scroll to the view at the position, anchored at the top of the screen. We call the scroll
         // method on the LayoutManager directly since it is not exposed by RecyclerView.
         layoutManager.scrollToPositionWithOffset(lastScrollSection.appItem.position, 0);
@@ -397,7 +386,7 @@
     }
 
     /**
-     * Returns the bounds for the scrollbar.
+     * Updates the bounds for the scrollbar.
      */
     private void updateVerticalScrollbarBounds() {
         List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
@@ -411,19 +400,18 @@
         // Find the index and height of the first visible row (all rows have the same height)
         int x;
         int y;
-        boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
-                LAYOUT_DIRECTION_RTL);
+        int predictionBarHeight = mApps.getPredictedApps().isEmpty() ? 0 : mPredictionBarHeight;
         int rowCount = getNumRows();
         getCurScrollState(mScrollPosState, items);
         if (mScrollPosState.rowIndex != -1) {
             int height = getHeight() - getPaddingTop() - getPaddingBottom();
-            int totalScrollHeight = rowCount * mScrollPosState.rowHeight + mPredictionBarHeight;
+            int totalScrollHeight = rowCount * mScrollPosState.rowHeight + predictionBarHeight;
             if (totalScrollHeight > height) {
                 int scrollbarHeight = Math.max(mScrollbarMinHeight,
                         (int) (height / ((float) totalScrollHeight / height)));
 
                 // Calculate the position and size of the scroll bar
-                if (isRtl) {
+                if (Utilities.isRtl(getResources())) {
                     x = mBackgroundPadding.left;
                 } else {
                     x = getWidth() - mBackgroundPadding.right - mScrollbarWidth;
@@ -433,7 +421,7 @@
                 // that the user has already scrolled and then map that to the scroll bar bounds
                 int availableY = totalScrollHeight - height;
                 int availableScrollY = height - scrollbarHeight;
-                y = (mScrollPosState.rowIndex * mScrollPosState.rowHeight) + mPredictionBarHeight
+                y = (mScrollPosState.rowIndex * mScrollPosState.rowHeight) + predictionBarHeight
                         - mScrollPosState.rowTopOffset;
                 y = getPaddingTop() +
                         (int) (((float) (getPaddingTop() + y) / availableY) * availableScrollY);
@@ -446,6 +434,30 @@
     }
 
     /**
+     * Updates the bounds for the fast scroller.
+     */
+    private void updateFastScrollerBounds() {
+        if (mFastScrollAlpha > 0f && !mFastScrollSectionName.isEmpty()) {
+            int x;
+            int y;
+
+            // Calculate the position for the fast scroller popup
+            Rect bgBounds = mFastScrollerBg.getBounds();
+            if (Utilities.isRtl(getResources())) {
+                x = mBackgroundPadding.left + getScrollBarSize();
+            } else {
+                x = getWidth() - getPaddingRight() - getScrollBarSize() - bgBounds.width();
+            }
+            y = mLastY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgBounds.height());
+            y = Math.max(getPaddingTop(), Math.min(y, getHeight() - getPaddingBottom() -
+                    bgBounds.height()));
+            mFastScrollerBounds.set(x, y, x + bgBounds.width(), y + bgBounds.height());
+        } else {
+            mFastScrollerBounds.setEmpty();
+        }
+    }
+
+    /**
      * Returns the row index for a app index in the list.
      */
     private int findRowForAppIndex(int index) {
@@ -477,19 +489,6 @@
     }
 
     /**
-     * Forces a refresh of the scroll position to any scroll listener.
-     */
-    private void refreshCurScrollPosition() {
-        List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
-        getCurScrollState(mScrollPosState, items);
-        if (mScrollPosState.rowIndex != -1) {
-            int scrollY = getPaddingTop() + (mScrollPosState.rowIndex * mScrollPosState.rowHeight) +
-                    mPredictionBarHeight - mScrollPosState.rowTopOffset;
-            updateScrollY(scrollY);
-        }
-    }
-
-    /**
      * Returns the current scroll state.
      */
     private void getCurScrollState(ScrollPositionState stateOut,
diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java
index a74670b..5ccb6c6 100644
--- a/src/com/android/launcher3/AppsContainerView.java
+++ b/src/com/android/launcher3/AppsContainerView.java
@@ -15,12 +15,16 @@
  */
 package com.android.launcher3;
 
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.InsetDrawable;
+import android.os.Build;
 import android.support.v7.widget.RecyclerView;
 import android.text.Editable;
 import android.text.TextWatcher;
@@ -31,6 +35,7 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.FrameLayout;
@@ -43,21 +48,104 @@
 
 
 /**
+ * Interface for controlling the header elevation in response to RecyclerView scroll.
+ */
+interface HeaderElevationController {
+    void onScroll(int scrollY);
+    void disable();
+}
+
+/**
+ * Implementation of the header elevation mechanism for pre-L devices.  It simulates elevation
+ * by drawing a gradient under the header bar.
+ */
+final class HeaderElevationControllerV16 implements HeaderElevationController {
+
+    private final View mShadow;
+
+    private final float mScrollToElevation;
+
+    public HeaderElevationControllerV16(View header) {
+        Resources res = header.getContext().getResources();
+        mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
+
+        mShadow = new View(header.getContext());
+        mShadow.setBackground(new GradientDrawable(
+                GradientDrawable.Orientation.TOP_BOTTOM, new int[] {0x44000000, 0x00000000}));
+        mShadow.setAlpha(0);
+
+        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.MATCH_PARENT,
+                res.getDimensionPixelSize(R.dimen.all_apps_header_shadow_height));
+        lp.topMargin = ((FrameLayout.LayoutParams) header.getLayoutParams()).height;
+
+        ((ViewGroup) header.getParent()).addView(mShadow, lp);
+    }
+
+    @Override
+    public void onScroll(int scrollY) {
+        float elevationPct = (float) Math.min(scrollY, mScrollToElevation) /
+                mScrollToElevation;
+        mShadow.setAlpha(elevationPct);
+    }
+
+    @Override
+    public void disable() {
+        ViewGroup parent = (ViewGroup) mShadow.getParent();
+        if (parent != null) {
+            parent.removeView(mShadow);
+        }
+    }
+}
+
+/**
+ * Implementation of the header elevation mechanism for L+ devices, which makes use of the native
+ * view elevation.
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+final class HeaderElevationControllerVL implements HeaderElevationController {
+
+    private final View mHeader;
+    private final float mMaxElevation;
+    private final float mScrollToElevation;
+
+    public HeaderElevationControllerVL(View header) {
+        mHeader = header;
+
+        Resources res = header.getContext().getResources();
+        mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation);
+        mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
+    }
+
+    @Override
+    public void onScroll(int scrollY) {
+        float elevationPct = (float) Math.min(scrollY, mScrollToElevation) /
+                mScrollToElevation;
+        float newElevation = mMaxElevation * elevationPct;
+        if (Float.compare(mHeader.getElevation(), newElevation) != 0) {
+            mHeader.setElevation(newElevation);
+        }
+    }
+
+    @Override
+    public void disable() { }
+}
+
+/**
  * The all apps view container.
  */
 public class AppsContainerView extends BaseContainerView implements DragSource, Insettable,
         TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable,
         AlphabeticalAppsList.FilterChangedCallback, AppsGridAdapter.PredictionBarSpacerCallbacks,
-        View.OnTouchListener, View.OnClickListener, View.OnLongClickListener {
+        View.OnTouchListener, View.OnClickListener, View.OnLongClickListener,
+        ViewTreeObserver.OnPreDrawListener {
 
     public static final boolean GRID_MERGE_SECTIONS = true;
 
     private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
     private static final boolean DYNAMIC_HEADER_ELEVATION = true;
     private static final boolean DISMISS_SEARCH_ON_BACK = true;
-    private static final float HEADER_ELEVATION_DP = 4;
-    // How far the user has to scroll in order to reach the full elevation
-    private static final float HEADER_SCROLL_TO_ELEVATION_DP = 16;
+
     private static final int FADE_IN_DURATION = 175;
     private static final int FADE_OUT_DURATION = 100;
     private static final int SEARCH_TRANSLATION_X_DP = 18;
@@ -80,6 +168,8 @@
     private View mDismissSearchButtonView;
     private AppsContainerSearchEditTextView mSearchBarEditView;
 
+    private HeaderElevationController mElevationController;
+
     private int mNumAppsPerRow;
     private int mNumPredictedAppsPerRow;
     // This coordinate is relative to this container view
@@ -92,8 +182,7 @@
     // Normal container insets
     private int mContainerInset;
     private int mPredictionBarHeight;
-    // RecyclerView scroll position
-    @Thunk int mRecyclerViewScrollY;
+    private int mLastRecyclerViewScrollPos = -1;
 
     private CheckLongPressHelper mPredictionIconCheckForLongPress;
     private View mPredictionIconUnderTouch;
@@ -171,6 +260,7 @@
      */
     public void hideHeaderBar() {
         mHeaderView.setVisibility(View.GONE);
+        mElevationController.disable();
         onUpdateBackgrounds();
         onUpdatePaddings();
     }
@@ -198,8 +288,7 @@
 
     @Override
     protected void onFinishInflate() {
-        boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
-                LAYOUT_DIRECTION_RTL);
+        boolean isRtl = Utilities.isRtl(getResources());
         mAdapter.setRtl(isRtl);
 
         // Work around the search box getting first focus and showing the cursor by
@@ -217,9 +306,13 @@
         // Fix the header view elevation if not dynamically calculating it
         mHeaderView = findViewById(R.id.header);
         mHeaderView.setOnClickListener(this);
-        if (Utilities.isLmpOrAbove() && !DYNAMIC_HEADER_ELEVATION) {
-            mHeaderView.setElevation(DynamicGrid.pxFromDp(HEADER_ELEVATION_DP,
-                getContext().getResources().getDisplayMetrics()));
+
+        mElevationController = Utilities.isLmpOrAbove() ?
+                new HeaderElevationControllerVL(mHeaderView) :
+                    new HeaderElevationControllerV16(mHeaderView);
+        if (!DYNAMIC_HEADER_ELEVATION) {
+            mElevationController.onScroll(getResources()
+                    .getDimensionPixelSize(R.dimen.all_apps_header_scroll_to_elevation));
         }
 
         // Fix the prediction bar size
@@ -258,14 +351,6 @@
         mAppsRecyclerView.setLayoutManager(mLayoutManager);
         mAppsRecyclerView.setAdapter(mAdapter);
         mAppsRecyclerView.setHasFixedSize(true);
-        mAppsRecyclerView.setOnScrollListenerProxy(
-                new BaseContainerRecyclerView.OnScrollToListener() {
-                    @Override
-                    public void onScrolledTo(int x, int y) {
-                        mRecyclerViewScrollY = y;
-                        onRecyclerViewScrolled();
-                    }
-                });
         if (mItemDecoration != null) {
             mAppsRecyclerView.addItemDecoration(mItemDecoration);
         }
@@ -275,9 +360,7 @@
 
     @Override
     public void onBindPredictionBar() {
-        if (!updatePredictionBarVisibility()) {
-            return;
-        }
+        updatePredictionBarVisibility();
 
         List<AppInfo> predictedApps = mApps.getPredictedApps();
         int childCount = mPredictionBarView.getChildCount();
@@ -329,11 +412,14 @@
      */
     @Override
     protected void onUpdatePaddings() {
-        boolean isRtl = (getResources().getConfiguration().getLayoutDirection() ==
-                LAYOUT_DIRECTION_RTL);
+        boolean isRtl = Utilities.isRtl(getResources());
         boolean hasSearchBar = (mSearchBarEditView != null) &&
                 (mSearchBarEditView.getVisibility() == View.VISIBLE);
 
+        // Set the background on the container, but let the recyclerView extend the full screen,
+        // so that the fast-scroller works on the edge as well.
+        mContentView.setPadding(0, 0, 0, 0);
+
         if (mFixedBounds.isEmpty()) {
             // If there are no fixed bounds, then use the default padding and insets
             setPadding(mInsets.left, mContainerInset + mInsets.top, mInsets.right,
@@ -376,19 +462,23 @@
     @Override
     protected void onUpdateBackgrounds() {
         int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
-        boolean hasSearchBar = (mSearchBarEditView != null) &&
-                (mSearchBarEditView.getVisibility() == View.VISIBLE);
 
         // Update the background of the reveal view and list to be inset with the fixed bound
         // insets instead of the default insets
         // TODO: Use quantum_panel instead of quantum_panel_shape.
-        mAppsRecyclerView.setBackground(new InsetDrawable(
-                getContext().getResources().getDrawable(
-                        hasSearchBar ? R.drawable.apps_list_search_bg : R.drawable.quantum_panel_shape),
-                inset, 0, inset, 0));
-        getRevealView().setBackground(new InsetDrawable(
+        InsetDrawable background = new InsetDrawable(
                 getContext().getResources().getDrawable(R.drawable.quantum_panel_shape),
-                inset, 0, inset, 0));
+                inset, 0, inset, 0);
+        mContentView.setBackground(background);
+        mAppsRecyclerView.updateBackgroundPadding(background);
+        mAdapter.updateBackgroundPadding(background);
+        getRevealView().setBackground(background.getConstantState().newDrawable());
+    }
+
+    @Override
+    public boolean onPreDraw() {
+        synchronizeToRecyclerViewScrollPosition(mAppsRecyclerView.getScrollPosition());
+        return true;
     }
 
     @Override
@@ -396,11 +486,13 @@
         return handleTouchEvent(ev);
     }
 
+    @SuppressLint("ClickableViewAccessibility")
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         return handleTouchEvent(ev);
     }
 
+    @SuppressLint("ClickableViewAccessibility")
     @Override
     public boolean onTouch(View v, MotionEvent ev) {
         switch (ev.getAction()) {
@@ -589,7 +681,11 @@
 
     @Override
     public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
-        // Do nothing
+        // Register for a pre-draw listener to synchronize the recycler view scroll to other views
+        // in this container
+        if (!toWorkspace) {
+            getViewTreeObserver().addOnPreDrawListener(this);
+        }
     }
 
     @Override
@@ -609,26 +705,26 @@
                 hideSearchField(false, false);
             }
         }
+        if (toWorkspace) {
+            getViewTreeObserver().removeOnPreDrawListener(this);
+            mLastRecyclerViewScrollPos = -1;
+        }
     }
 
     /**
      * Updates the container when the recycler view is scrolled.
      */
-    private void onRecyclerViewScrolled() {
-        if (DYNAMIC_HEADER_ELEVATION && Utilities.isLmpOrAbove()) {
-            int elevation = DynamicGrid.pxFromDp(HEADER_ELEVATION_DP,
-                    getContext().getResources().getDisplayMetrics());
-            int scrollToElevation = DynamicGrid.pxFromDp(HEADER_SCROLL_TO_ELEVATION_DP,
-                    getContext().getResources().getDisplayMetrics());
-            float elevationPct = (float) Math.min(mRecyclerViewScrollY, scrollToElevation) /
-                    scrollToElevation;
-            float newElevation = elevation * elevationPct;
-            if (Float.compare(mHeaderView.getElevation(), newElevation) != 0) {
-                mHeaderView.setElevation(newElevation);
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+    private void synchronizeToRecyclerViewScrollPosition(int scrollY) {
+        if (mLastRecyclerViewScrollPos != scrollY) {
+            mLastRecyclerViewScrollPos = scrollY;
+            if (DYNAMIC_HEADER_ELEVATION) {
+                mElevationController.onScroll(scrollY);
             }
-        }
 
-        mPredictionBarView.setTranslationY(-mRecyclerViewScrollY + mAppsRecyclerView.getPaddingTop());
+            // Scroll the prediction bar with the contents of the recycler view
+            mPredictionBarView.setTranslationY(-scrollY + mAppsRecyclerView.getPaddingTop());
+        }
     }
 
     @Override
@@ -656,12 +752,14 @@
                 // We workaround the fact that the recycler view needs the touches for the scroll
                 // and we want to intercept it for clicks in the prediction bar by handling clicks
                 // and long clicks in the prediction bar ourselves.
-                mPredictionIconTouchDownPos.set(x, y);
-                mPredictionIconUnderTouch = findPredictedAppAtCoordinate(x, y);
-                if (mPredictionIconUnderTouch != null) {
-                    mPredictionIconCheckForLongPress =
-                            new CheckLongPressHelper(mPredictionIconUnderTouch, this);
-                    mPredictionIconCheckForLongPress.postCheckForLongPress();
+                if (mPredictionBarView != null && mPredictionBarView.getVisibility() == View.VISIBLE) {
+                    mPredictionIconTouchDownPos.set(x, y);
+                    mPredictionIconUnderTouch = findPredictedAppAtCoordinate(x, y);
+                    if (mPredictionIconUnderTouch != null) {
+                        mPredictionIconCheckForLongPress =
+                                new CheckLongPressHelper(mPredictionIconUnderTouch, this);
+                        mPredictionIconCheckForLongPress.postCheckForLongPress();
+                    }
                 }
 
                 if (!mFixedBounds.isEmpty()) {
@@ -681,6 +779,19 @@
                     }
                 }
                 break;
+            case MotionEvent.ACTION_MOVE:
+                if (mPredictionIconUnderTouch != null) {
+                    float dist = (float) Math.hypot(x - mPredictionIconTouchDownPos.x,
+                            y - mPredictionIconTouchDownPos.y);
+                    if (dist > ViewConfiguration.get(getContext()).getScaledTouchSlop()) {
+                        if (mPredictionIconCheckForLongPress != null) {
+                            mPredictionIconCheckForLongPress.cancelLongPress();
+                        }
+                        mPredictionIconCheckForLongPress = null;
+                        mPredictionIconUnderTouch = null;
+                    }
+                }
+                break;
             case MotionEvent.ACTION_UP:
                 if (mBoundsCheckLastTouchDownPos.x > -1) {
                     ViewConfiguration viewConfig = ViewConfiguration.get(getContext());
@@ -723,11 +834,25 @@
      * Returns the predicted app in the prediction bar given a set of local coordinates.
      */
     private View findPredictedAppAtCoordinate(int x, int y) {
-        int[] coord = {x, y};
         Rect hitRect = new Rect();
+
+        // Ensure we aren't hitting the search bar
+        int[] coord = {x, y};
+        Utilities.mapCoordInSelfToDescendent(mHeaderView, this, coord);
+        mHeaderView.getHitRect(hitRect);
+        if (hitRect.contains(coord[0], coord[1])) {
+            return null;
+        }
+
+        // Check against the children of the prediction bar
+        coord[0] = x;
+        coord[1] = y;
         Utilities.mapCoordInSelfToDescendent(mPredictionBarView, this, coord);
         for (int i = 0; i < mPredictionBarView.getChildCount(); i++) {
             View child = mPredictionBarView.getChildAt(i);
+            if (child.getVisibility() != View.VISIBLE) {
+                continue;
+            }
             child.getHitRect(hitRect);
             if (hitRect.contains(coord[0], coord[1])) {
                 return child;
diff --git a/src/com/android/launcher3/AppsGridAdapter.java b/src/com/android/launcher3/AppsGridAdapter.java
index dfbfa01..580930c 100644
--- a/src/com/android/launcher3/AppsGridAdapter.java
+++ b/src/com/android/launcher3/AppsGridAdapter.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
 package com.android.launcher3;
 
 import android.content.Context;
@@ -6,6 +21,7 @@
 import android.graphics.Paint;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.RecyclerView;
@@ -112,9 +128,9 @@
 
                 if (shouldDrawItemDivider(holder, items) && !hasDrawnPredictedAppsDivider) {
                     // Draw the divider under the predicted apps
-                    parent.getBackground().getPadding(mTmpBounds);
                     int top = child.getTop() + child.getHeight();
-                    c.drawLine(mTmpBounds.left, top, parent.getWidth() - mTmpBounds.right, top,
+                    c.drawLine(mBackgroundPadding.left, top,
+                            parent.getWidth() - mBackgroundPadding.right, top,
                             mPredictedAppsDividerPaint);
                     hasDrawnPredictedAppsDivider = true;
 
@@ -265,6 +281,7 @@
     private View.OnTouchListener mTouchListener;
     private View.OnClickListener mIconClickListener;
     private View.OnLongClickListener mIconLongClickListener;
+    @Thunk final Rect mBackgroundPadding = new Rect();
     @Thunk int mPredictionBarHeight;
     @Thunk int mAppsPerRow;
     @Thunk boolean mIsRtl;
@@ -341,6 +358,14 @@
     }
 
     /**
+     * Notifies the adapter of the background padding so that it can draw things correctly in the
+     * item decorator.
+     */
+    public void updateBackgroundPadding(Drawable background) {
+        background.getPadding(mBackgroundPadding);
+    }
+
+    /**
      * Returns the grid layout manager.
      */
     public GridLayoutManager getLayoutManager() {
diff --git a/src/com/android/launcher3/AppsRecyclerViewContainer.java b/src/com/android/launcher3/AppsRecyclerViewContainer.java
new file mode 100644
index 0000000..cf4beca
--- /dev/null
+++ b/src/com/android/launcher3/AppsRecyclerViewContainer.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.util.AttributeSet;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
+
+public class AppsRecyclerViewContainer extends FrameLayout implements BubbleTextShadowHandler {
+
+    private final ClickShadowView mTouchFeedbackView;
+
+    public AppsRecyclerViewContainer(Context context) {
+        this(context, null);
+    }
+
+    public AppsRecyclerViewContainer(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public AppsRecyclerViewContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+
+        LauncherAppState app = LauncherAppState.getInstance();
+        DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+
+        mTouchFeedbackView = new ClickShadowView(context);
+
+        // Make the feedback view large enough to hold the blur bitmap.
+        int size = grid.allAppsIconSizePx + mTouchFeedbackView.getExtraSize();
+        addView(mTouchFeedbackView, size, size);
+    }
+
+    @Override
+    public void setPressedIcon(BubbleTextView icon, Bitmap background) {
+        if (icon == null || background == null) {
+            mTouchFeedbackView.setBitmap(null);
+            mTouchFeedbackView.animate().cancel();
+        } else if (mTouchFeedbackView.setBitmap(background)) {
+            mTouchFeedbackView.alignWithIconView(icon, (ViewGroup) icon.getParent());
+            mTouchFeedbackView.animateShadow();
+        }
+    }
+}
diff --git a/src/com/android/launcher3/BaseContainerRecyclerView.java b/src/com/android/launcher3/BaseContainerRecyclerView.java
index 59e20ca..e52d887 100644
--- a/src/com/android/launcher3/BaseContainerRecyclerView.java
+++ b/src/com/android/launcher3/BaseContainerRecyclerView.java
@@ -29,20 +29,11 @@
 public class BaseContainerRecyclerView extends RecyclerView
         implements RecyclerView.OnItemTouchListener {
 
-    /**
-     * Listener to get notified when the absolute scroll changes.
-     */
-    public interface OnScrollToListener {
-        void onScrolledTo(int x, int y);
-    }
-
     private static final int SCROLL_DELTA_THRESHOLD_DP = 4;
 
     /** Keeps the last known scrolling delta/velocity along y-axis. */
     @Thunk int mDy = 0;
-    @Thunk int mScrollY;
     private float mDeltaThreshold;
-    private OnScrollToListener mScrollToListener;
 
     public BaseContainerRecyclerView(Context context) {
         this(context, null);
@@ -68,20 +59,9 @@
         @Override
         public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
             mDy = dy;
-            mScrollY += dy;
-            if (mScrollToListener != null) {
-                mScrollToListener.onScrolledTo(0, mScrollY);
-            }
         }
     }
 
-    /**
-     * Sets an additional scroll listener, only needed for LMR1 version of the support lib.
-     */
-    public void setOnScrollListenerProxy(OnScrollToListener listener) {
-        mScrollToListener = listener;
-    }
-
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
@@ -106,17 +86,6 @@
     }
 
     /**
-     * Updates the scroll position, used to workaround a RecyclerView issue with scrolling to
-     * position.
-     */
-    protected void updateScrollY(int scrollY) {
-        mScrollY = scrollY;
-        if (mScrollToListener != null) {
-            mScrollToListener.onScrolledTo(0, mScrollY);
-        }
-    }
-
-    /**
      * Returns whether this {@link MotionEvent} should trigger the scroll to be stopped.
      */
     protected boolean shouldStopScroll(MotionEvent ev) {
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index d32c919..3b3b9bf 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -25,12 +26,15 @@
 import android.graphics.Canvas;
 import android.graphics.Region;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 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.view.ViewParent;
 import android.widget.TextView;
 
 import com.android.launcher3.IconCache.IconLoadRequest;
@@ -74,6 +78,7 @@
 
     private boolean mStayPressed;
     private boolean mIgnorePressedStateChange;
+    private boolean mDisableRelayout = false;
 
     private IconLoadRequest mIconLoadRequest;
 
@@ -272,9 +277,10 @@
         }
 
         // 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);
+        ViewParent parent = getParent();
+        if (parent != null && parent.getParent() instanceof BubbleTextShadowHandler) {
+            ((BubbleTextShadowHandler) parent.getParent()).setPressedIcon(
+                    this, mPressedBackground);
         }
 
         updateIconState();
@@ -444,25 +450,38 @@
     /**
      * Sets the icon for this view based on the layout direction.
      */
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     private Drawable setIcon(Drawable icon, int iconSize) {
         mIcon = icon;
         if (iconSize != -1) {
             mIcon.setBounds(0, 0, iconSize, iconSize);
         }
         if (mLayoutHorizontal) {
-            setCompoundDrawablesRelative(mIcon, null, null, null);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+                setCompoundDrawablesRelative(mIcon, null, null, null);
+            } else {
+                setCompoundDrawables(mIcon, null, null, null);
+            }
         } else {
-            setCompoundDrawablesRelative(null, mIcon, null, null);
+            setCompoundDrawables(null, mIcon, null, null);
         }
         return icon;
     }
 
+    @Override
+    public void requestLayout() {
+        if (!mDisableRelayout) {
+            super.requestLayout();
+        }
+    }
+
     /**
      * Applies the item info if it is same as what the view is pointing to currently.
      */
     public void reapplyItemInfo(final ItemInfo info) {
         if (getTag() == info) {
             mIconLoadRequest = null;
+            mDisableRelayout = true;
             if (info instanceof AppInfo) {
                 applyFromApplicationInfo((AppInfo) info);
             } else if (info instanceof ShortcutInfo) {
@@ -471,6 +490,7 @@
             } else if (info instanceof PackageItemInfo) {
                 applyFromPackageItemInfo((PackageItemInfo) info);
             }
+            mDisableRelayout = false;
         }
     }
 
@@ -502,4 +522,11 @@
             }
         }
     }
+
+    /**
+     * Interface to be implemented by the grand parent to allow click shadow effect.
+     */
+    public static interface BubbleTextShadowHandler {
+        void setPressedIcon(BubbleTextView icon, Bitmap background);
+    }
 }
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 683c511..b8214d1 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -83,6 +83,7 @@
         }
     }
 
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     protected void setDrawable(int resId) {
         // Get the hover color
         mDrawable = (TransitionDrawable) getCurrentDrawable();
@@ -90,7 +91,11 @@
         if (mDrawable == null) {
             // TODO: investigate why this is ever happening. Presently only on one known device.
             mDrawable = (TransitionDrawable) getResources().getDrawable(resId);
-            setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+                setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
+            } else {
+                setCompoundDrawablesWithIntrinsicBounds(mDrawable, null, null, null);
+            }
         }
 
         if (null != mDrawable) {
@@ -107,7 +112,7 @@
     }
 
     protected Drawable getCurrentDrawable() {
-        Drawable[] drawables = getCompoundDrawablesRelative();
+        Drawable[] drawables = getCompoundDrawables();
         for (int i = 0; i < drawables.length; ++i) {
             if (drawables[i] != null) {
                 return drawables[i];
@@ -241,10 +246,6 @@
         outRect.offsetTo(coords[0], coords[1]);
     }
 
-    private boolean isRtl() {
-        return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
-    }
-
     protected Rect getIconRect(int viewWidth, int viewHeight, int drawableWidth, int drawableHeight) {
         DragLayer dragLayer = mLauncher.getDragLayer();
 
@@ -258,7 +259,7 @@
         final int left;
         final int right;
 
-        if (isRtl()) {
+        if (Utilities.isRtl(getResources())) {
             right = to.right - getPaddingRight();
             left = right - width;
         } else {
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 72eabf1..edcdc11 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -47,6 +47,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.DecelerateInterpolator;
 
+import com.android.launcher3.BubbleTextView.BubbleTextShadowHandler;
 import com.android.launcher3.FolderIcon.FolderRingAnimator;
 import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
 import com.android.launcher3.accessibility.FolderAccessibilityHelper;
@@ -61,7 +62,7 @@
 import java.util.HashMap;
 import java.util.Stack;
 
-public class CellLayout extends ViewGroup {
+public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
     public static final int WORKSPACE_ACCESSIBILITY_DRAG = 2;
     public static final int FOLDER_ACCESSIBILITY_DRAG = 1;
 
@@ -132,7 +133,7 @@
     private int mDragOutlineCurrent = 0;
     private final Paint mDragOutlinePaint = new Paint();
 
-    private final FastBitmapView mTouchFeedbackView;
+    private final ClickShadowView mTouchFeedbackView;
 
     @Thunk HashMap<CellLayout.LayoutParams, Animator> mReorderAnimators = new
             HashMap<CellLayout.LayoutParams, Animator>();
@@ -301,9 +302,8 @@
         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));
+        mTouchFeedbackView = new ClickShadowView(context);
+        addView(mTouchFeedbackView);
         addView(mShortcutsAndWidgets);
     }
 
@@ -410,22 +410,15 @@
         invalidate();
     }
 
-    void setPressedIcon(BubbleTextView icon, Bitmap background, int padding) {
+    @Override
+    public void setPressedIcon(BubbleTextView icon, Bitmap background) {
         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();
+                mTouchFeedbackView.alignWithIconView(icon, mShortcutsAndWidgets);
+                mTouchFeedbackView.animateShadow();
             }
         }
     }
@@ -895,19 +888,20 @@
             mWidthGap = mOriginalWidthGap;
             mHeightGap = mOriginalHeightGap;
         }
-        int count = getChildCount();
-        int maxWidth = 0;
-        int maxHeight = 0;
-        for (int i = 0; i < count; i++) {
-            View child = getChildAt(i);
-            int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(newWidth,
-                    MeasureSpec.EXACTLY);
-            int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(newHeight,
-                    MeasureSpec.EXACTLY);
-            child.measure(childWidthMeasureSpec, childheightMeasureSpec);
-            maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
-            maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
-        }
+
+        // Make the feedback view large enough to hold the blur bitmap.
+        mTouchFeedbackView.measure(
+                MeasureSpec.makeMeasureSpec(mCellWidth + mTouchFeedbackView.getExtraSize(),
+                        MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(mCellHeight + mTouchFeedbackView.getExtraSize(),
+                        MeasureSpec.EXACTLY));
+
+        mShortcutsAndWidgets.measure(
+                MeasureSpec.makeMeasureSpec(newWidth, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.EXACTLY));
+
+        int maxWidth = mShortcutsAndWidgets.getMeasuredWidth();
+        int maxHeight = mShortcutsAndWidgets.getMeasuredHeight();
         if (mFixedWidth > 0 && mFixedHeight > 0) {
             setMeasuredDimension(maxWidth, maxHeight);
         } else {
@@ -921,13 +915,13 @@
                 (mCountX * mCellWidth);
         int left = getPaddingLeft() + (int) Math.ceil(offset / 2f);
         int top = getPaddingTop();
-        int count = getChildCount();
-        for (int i = 0; i < count; i++) {
-            View child = getChildAt(i);
-            child.layout(left, top,
-                    left + r - l,
-                    top + b - t);
-        }
+
+        mTouchFeedbackView.layout(left, top,
+                left + mTouchFeedbackView.getMeasuredWidth(),
+                top + mTouchFeedbackView.getMeasuredHeight());
+        mShortcutsAndWidgets.layout(left, top,
+                left + r - l,
+                top + b - t);
     }
 
     @Override
@@ -3018,7 +3012,7 @@
     // 2. When long clicking on an empty cell in a CellLayout, we save information about the
     //    cellX and cellY coordinates and which page was clicked. We then set this as a tag on
     //    the CellLayout that was long clicked
-    static final class CellInfo {
+    public static final class CellInfo {
         View cell;
         int cellX = -1;
         int cellY = -1;
@@ -3027,7 +3021,7 @@
         long screenId;
         long container;
 
-        CellInfo(View v, ItemInfo info) {
+        public CellInfo(View v, ItemInfo info) {
             cell = v;
             cellX = info.cellX;
             cellY = info.cellY;
diff --git a/src/com/android/launcher3/ClickShadowView.java b/src/com/android/launcher3/ClickShadowView.java
new file mode 100644
index 0000000..42fafe2
--- /dev/null
+++ b/src/com/android/launcher3/ClickShadowView.java
@@ -0,0 +1,110 @@
+/*
+ * 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.Color;
+import android.graphics.Paint;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class ClickShadowView extends View {
+
+    private static final int SHADOW_SIZE_FACTOR = 3;
+    private static final int SHADOW_LOW_ALPHA = 30;
+    private static final int SHADOW_HIGH_ALPHA = 60;
+
+    private final Paint mPaint;
+
+    private final float mShadowOffset;
+    private final float mShadowPadding;
+
+    private Bitmap mBitmap;
+
+    public ClickShadowView(Context context) {
+        super(context);
+        mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+        mPaint.setColor(Color.BLACK);
+
+        mShadowPadding = getResources().getDimension(R.dimen.blur_size_click_shadow);
+        mShadowOffset = getResources().getDimension(R.dimen.click_shadow_high_shift);
+    }
+
+    /**
+     * @return extra space required by the view to show the shadow.
+     */
+    public int getExtraSize() {
+        return (int) (SHADOW_SIZE_FACTOR * mShadowPadding);
+    }
+
+    /**
+     * Applies the new bitmap.
+     * @return true if the view was invalidated.
+     */
+    public boolean setBitmap(Bitmap b) {
+        if (b != mBitmap){
+            mBitmap = b;
+            invalidate();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mBitmap != null) {
+            mPaint.setAlpha(SHADOW_LOW_ALPHA);
+            canvas.drawBitmap(mBitmap, 0, 0, mPaint);
+            mPaint.setAlpha(SHADOW_HIGH_ALPHA);
+            canvas.drawBitmap(mBitmap, 0, mShadowOffset, mPaint);
+        }
+    }
+
+    public void animateShadow() {
+        setAlpha(0);
+        animate().alpha(1)
+            .setDuration(FastBitmapDrawable.CLICK_FEEDBACK_DURATION)
+            .setInterpolator(FastBitmapDrawable.CLICK_FEEDBACK_INTERPOLATOR)
+            .start();
+    }
+
+    /**
+     * Aligns the shadow with {@param view}
+     * @param viewParent immediate parent of {@param view}. It must be a sibling of this view.
+     */
+    public void alignWithIconView(BubbleTextView view, ViewGroup viewParent) {
+        float leftShift = view.getLeft() + viewParent.getLeft() - getLeft();
+        float topShift = view.getTop() + viewParent.getTop() - getTop();
+        int iconWidth = view.getRight() - view.getLeft();
+        int iconHSpace = iconWidth - view.getCompoundPaddingRight() - view.getCompoundPaddingLeft();
+        float drawableWidth = view.getIcon().getBounds().width();
+
+        setTranslationX(leftShift
+                + view.getCompoundPaddingLeft() * view.getScaleX()
+                + (iconHSpace - drawableWidth) * view.getScaleX() / 2  /* drawable gap */
+                + iconWidth * (1 - view.getScaleX()) / 2  /* gap due to scale */
+                - mShadowPadding  /* extra shadow size */
+                );
+        setTranslationY(topShift
+                + view.getPaddingTop() * view.getScaleY()  /* drawable gap */
+                + view.getHeight() * (1 - view.getScaleY()) / 2  /* gap due to scale */
+                - mShadowPadding  /* extra shadow size */
+                );
+    }
+}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 890b9c9..ad0afd9 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -443,7 +443,7 @@
         int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx;
         int numAppsCols = (availableAppsWidthPx - appsViewLeftMarginPx) /
                 (allAppsCellWidthPx + 2 * allAppsCellPaddingPx);
-        int numPredictiveAppCols = Math.max(numColumns, numAppsCols);
+        int numPredictiveAppCols = isPhone() ? 4 : numAppsCols;
         if ((numAppsCols != appsViewNumCols) ||
                 (numPredictiveAppCols != appsViewNumPredictiveCols)) {
             appsViewNumCols = numAppsCols;
@@ -459,11 +459,7 @@
         isLandscape = (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE);
         isTablet = resources.getBoolean(R.bool.is_tablet);
         isLargeTablet = resources.getBoolean(R.bool.is_large_tablet);
-        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            isLayoutRtl = (configuration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
-        } else {
-            isLayoutRtl = false;
-        }
+        isLayoutRtl = Utilities.isRtl(resources);
         widthPx = wPx;
         heightPx = hPx;
         availableWidthPx = awPx;
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java
index 5fea9d8..2191455 100644
--- a/src/com/android/launcher3/DragController.java
+++ b/src/com/android/launcher3/DragController.java
@@ -71,6 +71,7 @@
     // temporaries to avoid gc thrash
     private Rect mRectTemp = new Rect();
     private final int[] mCoordinatesTemp = new int[2];
+    private final boolean mIsRtl;
 
     /** Whether or not we're dragging. */
     private boolean mDragging;
@@ -157,6 +158,7 @@
         float density = r.getDisplayMetrics().density;
         mFlingToDeleteThresholdVelocity =
                 (int) (r.getInteger(R.integer.config_flingToDeleteMinVelocity) * density);
+        mIsRtl = Utilities.isRtl(r);
     }
 
     public boolean dragging() {
@@ -172,19 +174,18 @@
      * @param dragInfo The data associated with the object that is being dragged
      * @param dragAction The drag action: either {@link #DRAG_ACTION_MOVE} or
      *        {@link #DRAG_ACTION_COPY}
+     * @param viewImageBounds the position of the image inside the view
      * @param dragRegion Coordinates within the bitmap b for the position of item being dragged.
      *          Makes dragging feel more precise, e.g. you can clip out a transparent border
      */
-    public void startDrag(View v, Bitmap bmp, DragSource source, Object dragInfo, int dragAction,
-            Point extraPadding, float initialDragViewScale) {
+    public void startDrag(View v, Bitmap bmp, DragSource source, Object dragInfo,
+            Rect viewImageBounds, int dragAction, float initialDragViewScale) {
         int[] loc = mCoordinatesTemp;
         mLauncher.getDragLayer().getLocationInDragLayer(v, loc);
-        int viewExtraPaddingLeft = extraPadding != null ? extraPadding.x : 0;
-        int viewExtraPaddingTop = extraPadding != null ? extraPadding.y : 0;
-        int dragLayerX = loc[0] + v.getPaddingLeft() + viewExtraPaddingLeft +
-                (int) ((initialDragViewScale * bmp.getWidth() - bmp.getWidth()) / 2);
-        int dragLayerY = loc[1] + v.getPaddingTop() + viewExtraPaddingTop +
-                (int) ((initialDragViewScale * bmp.getHeight() - bmp.getHeight()) / 2);
+        int dragLayerX = loc[0] + viewImageBounds.left
+                + (int) ((initialDragViewScale * bmp.getWidth() - bmp.getWidth()) / 2);
+        int dragLayerY = loc[1] + viewImageBounds.top
+                + (int) ((initialDragViewScale * bmp.getHeight() - bmp.getHeight()) / 2);
 
         startDrag(bmp, dragLayerX, dragLayerY, source, dragInfo, dragAction, null,
                 null, initialDragViewScale, false);
@@ -548,9 +549,8 @@
         final int slop = ViewConfiguration.get(mLauncher).getScaledWindowTouchSlop();
         final int delay = mDistanceSinceScroll < slop ? RESCROLL_DELAY : SCROLL_DELAY;
         final DragLayer dragLayer = mLauncher.getDragLayer();
-        final boolean isRtl = (dragLayer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
-        final int forwardDirection = isRtl ? SCROLL_RIGHT : SCROLL_LEFT;
-        final int backwardsDirection = isRtl ? SCROLL_LEFT : SCROLL_RIGHT;
+        final int forwardDirection = mIsRtl ? SCROLL_RIGHT : SCROLL_LEFT;
+        final int backwardsDirection = mIsRtl ? SCROLL_LEFT : SCROLL_RIGHT;
 
         if (x < mScrollZone) {
             if (mScrollState == SCROLL_OUTSIDE_ZONE) {
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 8ba25da..423a9a3 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -39,6 +39,7 @@
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -48,33 +49,35 @@
  */
 public class DragLayer extends InsettableFrameLayout {
 
+    public static final int ANIMATION_END_DISAPPEAR = 0;
+    public static final int ANIMATION_END_FADE_OUT = 1;
+    public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
+
     // Scrim color without any alpha component.
     private static final int SCRIM_COLOR = Color.BLACK & 0x00FFFFFF;
 
+    private final int[] mTmpXY = new int[2];
+
     @Thunk DragController mDragController;
-    private int[] mTmpXY = new int[2];
 
     private int mXDown, mYDown;
     private Launcher mLauncher;
 
     // Variables relating to resizing widgets
-    private final ArrayList<AppWidgetResizeFrame> mResizeFrames =
-            new ArrayList<AppWidgetResizeFrame>();
+    private final ArrayList<AppWidgetResizeFrame> mResizeFrames = new ArrayList<>();
+    private final boolean mIsRtl;
     private AppWidgetResizeFrame mCurrentResizeFrame;
 
     // Variables relating to animation of views after drop
     private ValueAnimator mDropAnim = null;
     private ValueAnimator mFadeOutAnim = null;
-    private TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
+    private final TimeInterpolator mCubicEaseOutInterpolator = new DecelerateInterpolator(1.5f);
     @Thunk DragView mDropView = null;
     @Thunk int mAnchorViewInitialScrollX = 0;
     @Thunk View mAnchorView = null;
 
     private boolean mHoverPointClosesFolder = false;
-    private Rect mHitRect = new Rect();
-    public static final int ANIMATION_END_DISAPPEAR = 0;
-    public static final int ANIMATION_END_FADE_OUT = 1;
-    public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
+    private final Rect mHitRect = new Rect();
 
     private TouchCompleteListener mTouchCompleteListener;
 
@@ -86,6 +89,7 @@
     private float mBackgroundAlpha = 0;
 
     // Related to adjacent page hints
+    private final Rect mScrollChildPosition = new Rect();
     private boolean mInScrollArea;
     private boolean mShowPageHints;
     private Drawable mLeftHoverDrawable;
@@ -113,6 +117,7 @@
         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);
+        mIsRtl = Utilities.isRtl(res);
     }
 
     public void setup(Launcher launcher, DragController controller) {
@@ -912,6 +917,9 @@
 
     void showPageHints() {
         mShowPageHints = true;
+        Workspace workspace = mLauncher.getWorkspace();
+        getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.getChildCount() - 1),
+                mScrollChildPosition);
         invalidate();
     }
 
@@ -920,13 +928,6 @@
         invalidate();
     }
 
-    /**
-     * Note: this is a reimplementation of View.isLayoutRtl() since that is currently hidden api.
-     */
-    private boolean isLayoutRtl() {
-        return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
-    }
-
     @Override
     protected void dispatchDraw(Canvas canvas) {
         // Draw the background below children.
@@ -942,27 +943,22 @@
         if (mShowPageHints) {
             Workspace workspace = mLauncher.getWorkspace();
             int width = getMeasuredWidth();
-            Rect childRect = new Rect();
-            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);
+            CellLayout leftPage = (CellLayout) workspace.getChildAt(mIsRtl ? page + 1 : page - 1);
+            CellLayout rightPage = (CellLayout) workspace.getChildAt(mIsRtl ? page - 1 : page + 1);
 
             if (leftPage != null && leftPage.isDragTarget()) {
                 Drawable left = mInScrollArea && leftPage.getIsDragOverlapping() ?
                         mLeftHoverDrawableActive : mLeftHoverDrawable;
-                left.setBounds(0, childRect.top,
-                        left.getIntrinsicWidth(), childRect.bottom);
+                left.setBounds(0, mScrollChildPosition.top,
+                        left.getIntrinsicWidth(), mScrollChildPosition.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);
+                        mScrollChildPosition.top, width, mScrollChildPosition.bottom);
                 right.draw(canvas);
             }
         }
diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java
index 3eec3d9..120299e 100644
--- a/src/com/android/launcher3/DragView.java
+++ b/src/com/android/launcher3/DragView.java
@@ -76,6 +76,7 @@
      * @param registrationX The x coordinate of the registration point.
      * @param registrationY The y coordinate of the registration point.
      */
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     public DragView(Launcher launcher, Bitmap bitmap, int registrationX, int registrationY,
             int left, int top, int width, int height, final float initialScale) {
         super(launcher);
diff --git a/src/com/android/launcher3/FastBitmapView.java b/src/com/android/launcher3/FastBitmapView.java
deleted file mode 100644
index 0937eb7..0000000
--- a/src/com/android/launcher3/FastBitmapView.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.Context;
-import android.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/Folder.java b/src/com/android/launcher3/Folder.java
index 6e5941c..d0a7ba3 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -21,6 +21,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
+import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.res.Resources;
@@ -52,9 +53,9 @@
 import com.android.launcher3.CellLayout.CellInfo;
 import com.android.launcher3.DragController.DragListener;
 import com.android.launcher3.FolderInfo.FolderListener;
-import com.android.launcher3.LauncherAccessibilityDelegate.AccessibilityDragSource;
 import com.android.launcher3.UninstallDropTarget.UninstallSource;
 import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -330,6 +331,7 @@
     /**
      * We need to handle touch events to prevent them from falling through to the workspace below.
      */
+    @SuppressLint("ClickableViewAccessibility")
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         return true;
@@ -352,7 +354,7 @@
     /**
      * @return the FolderInfo object associated with this folder
      */
-    FolderInfo getInfo() {
+    public FolderInfo getInfo() {
         return mInfo;
     }
 
@@ -406,8 +408,9 @@
      *
      * @return A new UserFolder.
      */
-    static Folder fromXml(Context context) {
-        return (Folder) LayoutInflater.from(context).inflate(R.layout.user_folder, null);
+    @SuppressLint("InflateParams")
+    static Folder fromXml(Launcher launcher) {
+        return (Folder) launcher.getLayoutInflater().inflate(R.layout.user_folder, null);
     }
 
     /**
@@ -698,10 +701,10 @@
         boolean isOutsideLeftEdge = x < cellOverlap;
         boolean isOutsideRightEdge = x > (getWidth() - cellOverlap);
 
-        if (currentPage > 0 && (mContent.rtlLayout ? isOutsideRightEdge : isOutsideLeftEdge)) {
+        if (currentPage > 0 && (mContent.mIsRtl ? isOutsideRightEdge : isOutsideLeftEdge)) {
             showScrollHint(DragController.SCROLL_LEFT, d);
         } else if (currentPage < (mContent.getPageCount() - 1)
-                && (mContent.rtlLayout ? isOutsideLeftEdge : isOutsideRightEdge)) {
+                && (mContent.mIsRtl ? isOutsideLeftEdge : isOutsideRightEdge)) {
             showScrollHint(DragController.SCROLL_RIGHT, d);
         } else {
             mOnScrollHintAlarm.cancelAlarm();
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index a6494d2..de30b60 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -55,7 +55,7 @@
 
     private static final int[] sTempPosArray = new int[2];
 
-    public final boolean rtlLayout;
+    public final boolean mIsRtl;
 
     private final LayoutInflater mInflater;
     private final IconCache mIconCache;
@@ -89,7 +89,7 @@
         mInflater = LayoutInflater.from(context);
         mIconCache = app.getIconCache();
 
-        rtlLayout = getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+        mIsRtl = Utilities.isRtl(getResources());
         setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
     }
 
@@ -237,13 +237,13 @@
         page.setInvertIfRtl(true);
         page.setGridSize(mGridCountX, mGridCountY);
 
-        LayoutParams lp = generateDefaultLayoutParams();
-        lp.isFullScreenPage = true;
-        addView(page, -1, lp);
+        addView(page, -1, generateDefaultLayoutParams());
         return page;
     }
 
     public void setFixedSize(int width, int height) {
+        width -= (getPaddingLeft() + getPaddingRight());
+        height -= (getPaddingTop() + getPaddingBottom());
         for (int i = getChildCount() - 1; i >= 0; i --) {
             ((CellLayout) getChildAt(i)).setFixedSize(width, height);
         }
@@ -339,11 +339,13 @@
     }
 
     public int getDesiredWidth() {
-        return getPageCount() > 0 ? getPageAt(0).getDesiredWidth() : 0;
+        return getPageCount() > 0 ?
+                (getPageAt(0).getDesiredWidth() + getPaddingLeft() + getPaddingRight()) : 0;
     }
 
     public int getDesiredHeight()  {
-        return  getPageCount() > 0 ? getPageAt(0).getDesiredHeight() : 0;
+        return  getPageCount() > 0 ?
+                (getPageAt(0).getDesiredHeight() + getPaddingTop() + getPaddingBottom()) : 0;
     }
 
     public int getItemCount() {
@@ -439,7 +441,7 @@
      * Scrolls the current view by a fraction
      */
     public void showScrollHint(int direction) {
-        float fraction = (direction == DragController.SCROLL_LEFT) ^ rtlLayout
+        float fraction = (direction == DragController.SCROLL_LEFT) ^ mIsRtl
                 ? -SCROLL_HINT_FRACTION : SCROLL_HINT_FRACTION;
         int hint = (int) (fraction * getWidth());
         int scroll = getScrollForPage(getNextPage()) + hint;
@@ -596,7 +598,7 @@
                         }
                     };
                     v.animate()
-                        .translationXBy((direction > 0 ^ rtlLayout) ? -v.getWidth() : v.getWidth())
+                        .translationXBy((direction > 0 ^ mIsRtl) ? -v.getWidth() : v.getWidth())
                         .setDuration(REORDER_ANIMATION_DURATION)
                         .setStartDelay(0)
                         .withEndAction(endAction);
diff --git a/src/com/android/launcher3/HolographicOutlineHelper.java b/src/com/android/launcher3/HolographicOutlineHelper.java
index b1e0e68..5ff85d6 100644
--- a/src/com/android/launcher3/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/HolographicOutlineHelper.java
@@ -17,6 +17,7 @@
 package com.android.launcher3;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BlurMaskFilter;
 import android.graphics.Canvas;
@@ -25,11 +26,16 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
-import android.graphics.Region.Op;
+import android.graphics.drawable.Drawable;
+import android.util.SparseArray;
 
+/**
+ * Utility class to generate shadow and outline effect, which are used for click feedback
+ * and drag-n-drop respectively.
+ */
 public class HolographicOutlineHelper {
 
-    private static final Rect sTempRect = new Rect();
+    private static HolographicOutlineHelper sInstance;
 
     private final Canvas mCanvas = new Canvas();
     private final Paint mDrawPaint = new Paint();
@@ -40,26 +46,23 @@
     private final BlurMaskFilter mThinOuterBlurMaskFilter;
     private final BlurMaskFilter mMediumInnerBlurMaskFilter;
 
-    private final BlurMaskFilter mShaowBlurMaskFilter;
-    private final int mShadowOffset;
+    private final BlurMaskFilter mShadowBlurMaskFilter;
 
-    /**
-     * Padding used when creating shadow bitmap;
-     */
-    final int shadowBitmapPadding;
-
-    static HolographicOutlineHelper INSTANCE;
+    // We have 4 different icon sizes: homescreen, hotseat, folder & all-apps
+    private final SparseArray<Bitmap> mBitmapCache = new SparseArray<>(4);
 
     private HolographicOutlineHelper(Context context) {
-        final float scale = LauncherAppState.getInstance().getScreenDensity();
+        Resources res = context.getResources();
 
-        mMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER);
-        mThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER);
-        mMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL);
+        float mediumBlur = res.getDimension(R.dimen.blur_size_medium_outline);
+        mMediumOuterBlurMaskFilter = new BlurMaskFilter(mediumBlur, BlurMaskFilter.Blur.OUTER);
+        mMediumInnerBlurMaskFilter = new BlurMaskFilter(mediumBlur, BlurMaskFilter.Blur.NORMAL);
 
-        mShaowBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL);
-        mShadowOffset = (int) (scale * 2.0f);
-        shadowBitmapPadding = (int) (scale * 4.0f);
+        mThinOuterBlurMaskFilter = new BlurMaskFilter(
+                res.getDimension(R.dimen.blur_size_thin_outline), BlurMaskFilter.Blur.OUTER);
+
+        mShadowBlurMaskFilter = new BlurMaskFilter(
+                res.getDimension(R.dimen.blur_size_click_shadow), BlurMaskFilter.Blur.NORMAL);
 
         mDrawPaint.setFilterBitmap(true);
         mDrawPaint.setAntiAlias(true);
@@ -71,10 +74,10 @@
     }
 
     public static HolographicOutlineHelper obtain(Context context) {
-        if (INSTANCE == null) {
-            INSTANCE = new HolographicOutlineHelper(context);
+        if (sInstance == null) {
+            sInstance = new HolographicOutlineHelper(context);
         }
-        return INSTANCE;
+        return sInstance;
     }
 
     /**
@@ -153,51 +156,34 @@
     }
 
     Bitmap createMediumDropShadow(BubbleTextView view) {
-        final Bitmap result = Bitmap.createBitmap(
-                view.getWidth() + shadowBitmapPadding + shadowBitmapPadding,
-                view.getHeight() + shadowBitmapPadding + shadowBitmapPadding + mShadowOffset,
-                Bitmap.Config.ARGB_8888);
+        Drawable icon = view.getIcon();
+        if (icon == null) {
+            return null;
+        }
+        Rect rect = icon.getBounds();
 
-        mCanvas.setBitmap(result);
+        int bitmapWidth = (int) (rect.width() * view.getScaleX());
+        int bitmapHeight = (int) (rect.height() * view.getScaleY());
 
-        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);
+        int key = (bitmapWidth << 16) | bitmapHeight;
+        Bitmap cache = mBitmapCache.get(key);
+        if (cache == null) {
+            cache = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
+            mCanvas.setBitmap(cache);
+            mBitmapCache.put(key, cache);
+        } else {
+            mCanvas.setBitmap(cache);
+            mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
+        }
 
-        // 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.save(Canvas.MATRIX_SAVE_FLAG);
+        mCanvas.scale(view.getScaleX(), view.getScaleY());
+        mCanvas.translate(-rect.left, -rect.top);
+        icon.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;
+        mBlurPaint.setMaskFilter(mShadowBlurMaskFilter);
+        return cache.extractAlpha(mBlurPaint, null);
     }
 }
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index f1ff48d..f3383cc 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -16,8 +16,10 @@
 
 package com.android.launcher3;
 
+import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.Build;
 import android.provider.Settings;
 import android.util.AttributeSet;
 
@@ -68,10 +70,18 @@
         return source.supportsAppInfoDropTarget() && supportsDrop(getContext(), info);
     }
 
+    @SuppressWarnings("deprecation")
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     public static boolean supportsDrop(Context context, Object info) {
-        return (Settings.Global.getInt(context.getContentResolver(),
-                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1) &&
-                (info instanceof AppInfo || info instanceof PendingAddItemInfo);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            return (Settings.Global.getInt(context.getContentResolver(),
+                    Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1) &&
+                    (info instanceof AppInfo || info instanceof PendingAddItemInfo);
+        } else {
+            return (Settings.Secure.getInt(context.getContentResolver(),
+                    Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1) &&
+                    (info instanceof AppInfo || info instanceof PendingAddItemInfo);
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8603a35..7b7b617 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -22,6 +22,7 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
+import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -64,7 +65,6 @@
 import android.os.Message;
 import android.os.StrictMode;
 import android.os.SystemClock;
-import android.preference.PreferenceManager;
 import android.text.Selection;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
@@ -96,6 +96,7 @@
 
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.PagedView.PageSwitchListener;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.compat.LauncherActivityInfoCompat;
 import com.android.launcher3.compat.LauncherAppsCompat;
@@ -134,7 +135,7 @@
                    View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener,
                    LauncherStateTransitionAnimation.Callbacks {
     static final String TAG = "Launcher";
-    static final boolean LOGD = true;
+    static final boolean LOGD = false;
 
     // Temporary flag
     static final boolean DISABLE_ALL_APPS_SEARCH_INTEGRATION = true;
@@ -185,10 +186,6 @@
     private static final String RUNTIME_STATE_PENDING_ADD_CELL_X = "launcher.add_cell_x";
     // Type: int
     private static final String RUNTIME_STATE_PENDING_ADD_CELL_Y = "launcher.add_cell_y";
-    // Type: boolean
-    private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME = "launcher.rename_folder";
-    // Type: long
-    private static final String RUNTIME_STATE_PENDING_FOLDER_RENAME_ID = "launcher.rename_folder_id";
     // Type: int
     private static final String RUNTIME_STATE_PENDING_ADD_SPAN_X = "launcher.add_span_x";
     // Type: int
@@ -261,8 +258,6 @@
 
     private int[] mTmpAddItemCellCoordinates = new int[2];
 
-    private FolderInfo mFolderInfo;
-
     private Hotseat mHotseat;
     private ViewGroup mOverviewPanel;
 
@@ -480,11 +475,11 @@
             if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
                 // If the user leaves launcher, then we should just load items asynchronously when
                 // they return.
-                mModel.startLoader(true, PagedView.INVALID_RESTORE_PAGE);
+                mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
             } else {
                 // We only load the page synchronously if the user rotates (or triggers a
                 // configuration change) while launcher is in the foreground
-                mModel.startLoader(true, mWorkspace.getRestorePage());
+                mModel.startLoader(mWorkspace.getRestorePage());
             }
         }
 
@@ -809,7 +804,7 @@
         }
 
         boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
-                requestCode == REQUEST_CREATE_APPWIDGET);
+                requestCode == REQUEST_CREATE_APPWIDGET || requestCode == REQUEST_CREATE_SHORTCUT);
 
         final boolean workspaceLocked = isWorkspaceLocked();
         // We have special handling for widgets
@@ -1051,7 +1046,7 @@
         mPaused = false;
         if (mRestoring || mOnResumeNeedsLoad) {
             setWorkspaceLoading(true);
-            mModel.startLoader(true, PagedView.INVALID_RESTORE_PAGE);
+            mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
             mRestoring = false;
             mOnResumeNeedsLoad = false;
         }
@@ -1385,13 +1380,6 @@
             mRestoring = true;
         }
 
-        boolean renameFolder = savedState.getBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, false);
-        if (renameFolder) {
-            long id = savedState.getLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID);
-            mFolderInfo = mModel.getFolderById(this, sFolders, id);
-            mRestoring = true;
-        }
-
         mItemIdToViewId = (HashMap<Integer, Integer>)
                 savedState.getSerializable(RUNTIME_STATE_VIEW_IDS);
     }
@@ -1703,11 +1691,11 @@
                 updateAutoAdvanceState();
             } else if (ENABLE_DEBUG_INTENTS && DebugIntents.DELETE_DATABASE.equals(action)) {
                 mModel.resetLoadedState(false, true);
-                mModel.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
+                mModel.startLoader(PagedView.INVALID_RESTORE_PAGE,
                         LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE);
             } else if (ENABLE_DEBUG_INTENTS && DebugIntents.MIGRATE_DATABASE.equals(action)) {
                 mModel.resetLoadedState(false, true);
-                mModel.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
+                mModel.startLoader(PagedView.INVALID_RESTORE_PAGE,
                         LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
                                 | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
             }
@@ -2035,11 +2023,6 @@
             outState.putInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID, mPendingAddWidgetId);
         }
 
-        if (mFolderInfo != null && mWaitingForResult) {
-            outState.putBoolean(RUNTIME_STATE_PENDING_FOLDER_RENAME, true);
-            outState.putLong(RUNTIME_STATE_PENDING_FOLDER_RENAME_ID, mFolderInfo.id);
-        }
-
         // Save the current widgets tray?
         // TODO(hyunyoungs)
         outState.putSerializable(RUNTIME_STATE_VIEW_IDS, mItemIdToViewId);
@@ -2582,6 +2565,7 @@
         }
     }
 
+    @SuppressLint("ClickableViewAccessibility")
     public boolean onTouch(View v, MotionEvent event) {
         return false;
     }
@@ -2850,6 +2834,7 @@
     public View.OnTouchListener getHapticFeedbackTouchListener() {
         if (mHapticFeedbackTouchListener == null) {
             mHapticFeedbackTouchListener = new View.OnTouchListener() {
+                @SuppressLint("ClickableViewAccessibility")
                 @Override
                 public boolean onTouch(View v, MotionEvent event) {
                     if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
@@ -3200,7 +3185,7 @@
         }
     }
 
-    void closeFolder(Folder folder) {
+    public void closeFolder(Folder folder) {
         folder.getInfo().opened = false;
 
         ViewGroup parent = (ViewGroup) folder.getParent().getParent();
@@ -3353,7 +3338,7 @@
                 true);
     }
 
-    protected void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
+    public void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
         showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated,
                 onCompleteRunnable, true);
     }
@@ -3429,7 +3414,7 @@
      * Shows the widgets view.
      */
     void showWidgetsView(boolean animated, boolean resetPageToZero) {
-        Log.d(TAG, "showWidgetsView:" + animated + " resetPageToZero:" + resetPageToZero);
+        if (LOGD) Log.d(TAG, "showWidgetsView:" + animated + " resetPageToZero:" + resetPageToZero);
         if (resetPageToZero) {
             mWidgetsView.scrollToTop();
         }
@@ -3445,10 +3430,19 @@
 
     /**
      * Sets up the transition to show the apps/widgets view.
+     *
+     * @return whether the current from and to state allowed this operation
      */
-    private void showAppsOrWidgets(boolean animated, State toState) {
-        if (mState != State.WORKSPACE) return;
-        if (toState != State.APPS && toState != State.WIDGETS) return;
+    // TODO: calling method should use the return value so that when {@code false} is returned
+    // the workspace transition doesn't fall into invalid state.
+    private boolean showAppsOrWidgets(boolean animated, State toState) {
+        if (mState != State.WORKSPACE &&  mState != State.APPS_SPRING_LOADED &&
+                mState != State.WIDGETS_SPRING_LOADED) {
+            return false;
+        }
+        if (toState != State.APPS && toState != State.WIDGETS) {
+            return false;
+        }
 
         if (toState == State.APPS) {
             mStateTransitionAnimation.startAnimationToAllApps(animated);
@@ -3470,6 +3464,7 @@
         // Send an accessibility event to announce the context change
         getWindow().getDecorView()
                 .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+        return true;
     }
 
     /**
@@ -3485,8 +3480,7 @@
     }
 
     public void enterSpringLoadedDragMode() {
-        Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s",
-                mState.name()));
+        if (LOGD) Log.d(TAG, String.format("enterSpringLoadedDragMode [mState=%s", mState.name()));
         if (mState == State.WORKSPACE || mState == State.APPS_SPRING_LOADED ||
                 mState == State.WIDGETS_SPRING_LOADED) {
             return;
@@ -3681,7 +3675,7 @@
      */
     private boolean waitUntilResume(Runnable run, boolean deletePreviousRunnables) {
         if (mPaused) {
-            Log.i(TAG, "Deferring update until onResume");
+            if (LOGD) Log.d(TAG, "Deferring update until onResume");
             if (deletePreviousRunnables) {
                 while (mBindOnResumeCallbacks.remove(run)) {
                 }
@@ -3717,7 +3711,7 @@
      */
     public boolean setLoadOnResume() {
         if (mPaused) {
-            Log.i(TAG, "setLoadOnResume");
+            if (LOGD) Log.d(TAG, "setLoadOnResume");
             mOnResumeNeedsLoad = true;
             return true;
         } else {
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index be295f8..6ff7666 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -21,10 +21,11 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.os.Build;
 import android.view.View;
 import android.view.ViewAnimationUtils;
 import android.view.ViewTreeObserver;
-
 import java.util.HashSet;
 import java.util.WeakHashMap;
 
@@ -128,6 +129,7 @@
         return anim;
     }
 
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     public static Animator createCircularReveal(View view, int centerX,
             int centerY, float startRadius, float endRadius) {
         Animator anim = ViewAnimationUtils.createCircularReveal(view, centerX,
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index d51df32..f540eb4 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -31,13 +31,12 @@
 import android.view.Display;
 import android.view.WindowManager;
 
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
-import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
 import com.android.launcher3.util.Thunk;
 
 import java.lang.ref.WeakReference;
-import java.util.ArrayList;
 
 public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
 
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index af680f2..e19bc95 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -1,10 +1,12 @@
 package com.android.launcher3;
 
+import android.annotation.TargetApi;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Parcel;
 
 /**
@@ -32,6 +34,7 @@
         info.writeToParcel(p, 0);
         p.setDataPosition(0);
         LauncherAppWidgetProviderInfo lawpi = new LauncherAppWidgetProviderInfo(p);
+        p.recycle();
 
         int[] minResizeSpan = Launcher.getMinSpanForWidget(context, lawpi);
         int[] span = Launcher.getSpanForWidget(context, lawpi);
@@ -64,6 +67,7 @@
         minSpanY = widget.getMinSpanY();
     }
 
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     public String getLabel(PackageManager packageManager) {
         if (isCustomWidget) {
             return Utilities.trim(label);
@@ -71,6 +75,7 @@
         return super.loadLabel(packageManager);
     }
 
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     public Drawable getIcon(Context context, IconCache cache) {
         if (isCustomWidget) {
             return cache.getFullResIcon(provider.getPackageName(), icon);
diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java
index a2c56a9..c13752c 100644
--- a/src/com/android/launcher3/LauncherClings.java
+++ b/src/com/android/launcher3/LauncherClings.java
@@ -20,10 +20,12 @@
 import android.accounts.AccountManager;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
+import android.annotation.TargetApi;
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -34,7 +36,6 @@
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.view.accessibility.AccessibilityManager;
-
 import com.android.launcher3.util.Thunk;
 
 class LauncherClings implements OnClickListener {
@@ -70,7 +71,7 @@
             // Copy the shortcuts from the old database
             LauncherModel model = mLauncher.getModel();
             model.resetLoadedState(false, true);
-            model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
+            model.startLoader(PagedView.INVALID_RESTORE_PAGE,
                     LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
                             | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
             // Set the flag to skip the folder cling
@@ -212,6 +213,7 @@
     }
 
     /** Returns whether the clings are enabled or should be shown */
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
     private boolean areClingsEnabled() {
         if (DISABLE_CLINGS) {
             return false;
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 658a3e2..3e05f57 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -66,7 +66,6 @@
 import java.lang.ref.WeakReference;
 import java.net.URISyntaxException;
 import java.security.InvalidParameterException;
-import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -514,7 +513,7 @@
      * Adds the provided items to the workspace.
      */
     public void addAndBindAddedWorkspaceItems(final Context context,
-            final ArrayList<ItemInfo> workspaceApps) {
+            final ArrayList<? extends ItemInfo> workspaceApps) {
         final Callbacks callbacks = getCallback();
         if (workspaceApps.isEmpty()) {
             return;
@@ -793,7 +792,7 @@
     /**
      * Move an item in the DB to a new <container, screen, cellX, cellY>
      */
-    static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
+    public static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
             final long screenId, final int cellX, final int cellY) {
         item.container = container;
         item.cellX = cellX;
@@ -889,7 +888,7 @@
     /**
      * Update an item to the database in a specified container.
      */
-    static void updateItemInDatabase(Context context, final ItemInfo item) {
+    public static void updateItemInDatabase(Context context, final ItemInfo item) {
         final ContentValues values = new ContentValues();
         item.onAddToDatabase(context, values);
         updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
@@ -1346,40 +1345,32 @@
             }
         }
         if (runLoader) {
-            startLoader(false, PagedView.INVALID_RESTORE_PAGE);
+            startLoader(PagedView.INVALID_RESTORE_PAGE);
         }
     }
 
-    // If there is already a loader task running, tell it to stop.
-    // returns true if isLaunching() was true on the old task
-    private boolean stopLoaderLocked() {
-        boolean isLaunching = false;
+    /**
+     * If there is already a loader task running, tell it to stop.
+     */
+    private void stopLoaderLocked() {
         LoaderTask oldTask = mLoaderTask;
         if (oldTask != null) {
-            if (oldTask.isLaunching()) {
-                isLaunching = true;
-            }
             oldTask.stopLocked();
         }
-        return isLaunching;
     }
 
     public boolean isCurrentCallbacks(Callbacks callbacks) {
         return (mCallbacks != null && mCallbacks.get() == callbacks);
     }
 
-    public void startLoader(boolean isLaunching, int synchronousBindPage) {
-        startLoader(isLaunching, synchronousBindPage, LOADER_FLAG_NONE);
+    public void startLoader(int synchronousBindPage) {
+        startLoader(synchronousBindPage, LOADER_FLAG_NONE);
     }
 
-    public void startLoader(boolean isLaunching, int synchronousBindPage, int loadFlags) {
+    public void startLoader(int synchronousBindPage, int loadFlags) {
         // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
         InstallShortcutReceiver.enableInstallQueue();
         synchronized (mLock) {
-            if (DEBUG_LOADERS) {
-                Log.d(TAG, "startLoader isLaunching=" + isLaunching);
-            }
-
             // Clear any deferred bind-runnables from the synchronized load process
             // We must do this before any loading/binding is scheduled below.
             synchronized (mDeferredBindRunnables) {
@@ -1389,11 +1380,10 @@
             // Don't bother to start the thread if we know it's not going to do anything
             if (mCallbacks != null && mCallbacks.get() != null) {
                 // If there is already one running, tell it to stop.
-                // also, don't downgrade isLaunching if we're already running
-                isLaunching = isLaunching || stopLoaderLocked();
-                mLoaderTask = new LoaderTask(mApp.getContext(), isLaunching, loadFlags);
+                stopLoaderLocked();
+                mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags);
                 if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
-                        && mAllAppsLoaded && mWorkspaceLoaded) {
+                        && mAllAppsLoaded && mWorkspaceLoaded && !mIsLoaderTaskRunning) {
                     mLoaderTask.runBindSynchronousPage(synchronousBindPage);
                 } else {
                     sWorkerThread.setPriority(Thread.NORM_PRIORITY);
@@ -1484,22 +1474,16 @@
      */
     private class LoaderTask implements Runnable {
         private Context mContext;
-        private boolean mIsLaunching;
         @Thunk boolean mIsLoadingAndBindingWorkspace;
         private boolean mStopped;
         @Thunk boolean mLoadAndBindStepFinished;
         private int mFlags;
 
-        LoaderTask(Context context, boolean isLaunching, int flags) {
+        LoaderTask(Context context, int flags) {
             mContext = context;
-            mIsLaunching = isLaunching;
             mFlags = flags;
         }
 
-        boolean isLaunching() {
-            return mIsLaunching;
-        }
-
         boolean isLoadingWorkspace() {
             return mIsLoadingAndBindingWorkspace;
         }
@@ -1600,20 +1584,15 @@
 
         public void run() {
             synchronized (mLock) {
+                if (mStopped) {
+                    return;
+                }
                 mIsLoaderTaskRunning = true;
             }
             // Optimize for end-user experience: if the Launcher is up and // running with the
             // All Apps interface in the foreground, load All Apps first. Otherwise, load the
             // workspace first (default).
             keep_running: {
-                // Elevate priority when Home launches for the first time to avoid
-                // starving at boot time. Staring at a blank home is not cool.
-                synchronized (mLock) {
-                    if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
-                            (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
-                    android.os.Process.setThreadPriority(mIsLaunching
-                            ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
-                }
                 if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
                 loadAndBindWorkspace();
 
@@ -1621,24 +1600,11 @@
                     break keep_running;
                 }
 
-                // Whew! Hard work done.  Slow us down, and wait until the UI thread has
-                // settled down.
-                synchronized (mLock) {
-                    if (mIsLaunching) {
-                        if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to BACKGROUND");
-                        android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-                    }
-                }
                 waitForIdle();
 
                 // second step
                 if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
                 loadAndBindAllApps();
-
-                // Restore the default thread priority after we are done loading items
-                synchronized (mLock) {
-                    android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
-                }
             }
 
             // Clear out this reference, otherwise we end up holding it until all of the
@@ -2783,6 +2749,11 @@
             }
             if (!mAllAppsLoaded) {
                 loadAllApps();
+                synchronized (LoaderTask.this) {
+                    if (mStopped) {
+                        return;
+                    }
+                }
                 updateAllAppsIconsCache();
                 synchronized (LoaderTask.this) {
                     if (mStopped) {
@@ -2974,7 +2945,6 @@
         public void dumpState() {
             synchronized (sBgLock) {
                 Log.d(TAG, "mLoaderTask.mContext=" + mContext);
-                Log.d(TAG, "mLoaderTask.mIsLaunching=" + mIsLaunching);
                 Log.d(TAG, "mLoaderTask.mStopped=" + mStopped);
                 Log.d(TAG, "mLoaderTask.mLoadAndBindStepFinished=" + mLoadAndBindStepFinished);
                 Log.d(TAG, "mItems size=" + sBgWorkspaceItems.size());
@@ -3694,39 +3664,6 @@
         return folderInfo;
     }
 
-    public static class WidgetAndShortcutNameComparator implements Comparator<Object> {
-        private final AppWidgetManagerCompat mManager;
-        private final PackageManager mPackageManager;
-        private final HashMap<Object, String> mLabelCache;
-        private final Collator mCollator;
-
-        public WidgetAndShortcutNameComparator(Context context) {
-            mManager = AppWidgetManagerCompat.getInstance(context);
-            mPackageManager = context.getPackageManager();
-            mLabelCache = new HashMap<Object, String>();
-            mCollator = Collator.getInstance();
-        }
-        public final int compare(Object a, Object b) {
-            String labelA, labelB;
-            if (mLabelCache.containsKey(a)) {
-                labelA = mLabelCache.get(a);
-            } else {
-                labelA = (a instanceof LauncherAppWidgetProviderInfo)
-                        ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) a))
-                        : Utilities.trim(((ResolveInfo) a).loadLabel(mPackageManager));
-                mLabelCache.put(a, labelA);
-            }
-            if (mLabelCache.containsKey(b)) {
-                labelB = mLabelCache.get(b);
-            } else {
-                labelB = (b instanceof LauncherAppWidgetProviderInfo)
-                        ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) b))
-                        : Utilities.trim(((ResolveInfo) b).loadLabel(mPackageManager));
-                mLabelCache.put(b, labelB);
-            }
-            return mCollator.compare(labelA, labelB);
-        }
-    };
 
     static boolean isValidProvider(AppWidgetProviderInfo provider) {
         return (provider != null) && (provider.provider != null)
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index f9f5ae1..8bc8988 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -52,6 +52,7 @@
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.util.ManagedProfileHeuristic;
 import com.android.launcher3.util.Thunk;
 
 import java.io.File;
@@ -64,7 +65,7 @@
     private static final String TAG = "Launcher.LauncherProvider";
     private static final boolean LOGD = false;
 
-    private static final int DATABASE_VERSION = 24;
+    private static final int DATABASE_VERSION = 25;
 
     static final String OLD_AUTHORITY = "com.android.launcher2.settings";
     static final String AUTHORITY = ProviderConfig.AUTHORITY;
@@ -475,6 +476,9 @@
             // Fresh and clean launcher DB.
             mMaxItemId = initializeMaxItemId(db);
             setFlagEmptyDbCreated();
+
+            // When a new DB is created, remove all previously stored managed profile information.
+            ManagedProfileHeuristic.processAllUsers(Collections.EMPTY_LIST, mContext);
         }
 
         private void addWorkspacesTable(SQLiteDatabase db) {
@@ -620,7 +624,9 @@
                 }
                 case 23:
                     convertShortcutsToLauncherActivities(db);
-                case 24: {
+                case 24:
+                    ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mContext);
+                case 25: {
                     // DB Upgraded successfully
                     return;
                 }
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index 73ae51c..3243754 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -22,16 +22,17 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.TimeInterpolator;
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
 import android.content.res.Resources;
+import android.os.Build;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewAnimationUtils;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
-
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.widget.WidgetsContainerView;
-
 import java.util.HashMap;
 
 /**
@@ -227,6 +228,7 @@
     /**
      * Creates and starts a new animation to a particular overlay view.
      */
+    @SuppressLint("NewApi")
     private void startAnimationToOverlay(final Workspace.State toWorkspaceState, final View toView,
              final View contentView, final View revealView, final boolean animated,
              final boolean hideSearchBar, final PrivateTransitionCallbacks pCb) {
diff --git a/src/com/android/launcher3/MemoryTracker.java b/src/com/android/launcher3/MemoryTracker.java
index 2d37c80..067a50f 100644
--- a/src/com/android/launcher3/MemoryTracker.java
+++ b/src/com/android/launcher3/MemoryTracker.java
@@ -101,7 +101,7 @@
 
     public void startTrackingProcess(int pid, String name, long start) {
         synchronized (mLock) {
-            final Long lpid = new Long(pid);
+            final Long lpid = Long.valueOf(pid);
 
             if (mPids.contains(lpid)) return;
 
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index e6dc59c..686dd2f 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -221,6 +221,7 @@
     private static final Rect sTmpRect = new Rect();
 
     protected final Rect mInsets = new Rect();
+    protected final boolean mIsRtl;
 
     public interface PageSwitchListener {
         void onPageSwitch(View newPage, int newPageIndex);
@@ -248,6 +249,7 @@
         a.recycle();
 
         setHapticFeedbackEnabled(false);
+        mIsRtl = Utilities.isRtl(getResources());
         init();
     }
 
@@ -403,16 +405,9 @@
     }
 
     /**
-     * Note: this is a reimplementation of View.isLayoutRtl() since that is currently hidden api.
-     */
-    public boolean isLayoutRtl() {
-        return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
-    }
-
-    /**
      * Returns the index of the currently displayed page.
      */
-    int getCurrentPage() {
+    public int getCurrentPage() {
         return mCurrentPage;
     }
 
@@ -427,7 +422,7 @@
         return getChildCount();
     }
 
-    View getPageAt(int index) {
+    public View getPageAt(int index) {
         return getChildAt(index);
     }
 
@@ -586,16 +581,15 @@
             x = Math.max(x, mFreeScrollMinScrollX);
         }
 
-        final boolean isRtl = isLayoutRtl();
         mUnboundedScrollX = x;
 
-        boolean isXBeforeFirstPage = isRtl ? (x > mMaxScrollX) : (x < 0);
-        boolean isXAfterLastPage = isRtl ? (x < 0) : (x > mMaxScrollX);
+        boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0);
+        boolean isXAfterLastPage = mIsRtl ? (x < 0) : (x > mMaxScrollX);
         if (isXBeforeFirstPage) {
             super.scrollTo(0, y);
             if (mAllowOverScroll) {
                 mWasInOverscroll = true;
-                if (isRtl) {
+                if (mIsRtl) {
                     overScroll(x - mMaxScrollX);
                 } else {
                     overScroll(x);
@@ -605,7 +599,7 @@
             super.scrollTo(mMaxScrollX, y);
             if (mAllowOverScroll) {
                 mWasInOverscroll = true;
-                if (isRtl) {
+                if (mIsRtl) {
                     overScroll(x);
                 } else {
                     overScroll(x - mMaxScrollX);
@@ -698,15 +692,35 @@
             super(width, height);
         }
 
+        public LayoutParams(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
         public LayoutParams(ViewGroup.LayoutParams source) {
             super(source);
         }
     }
 
+    @Override
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new LayoutParams(getContext(), attrs);
+    }
+
+    @Override
     protected LayoutParams generateDefaultLayoutParams() {
         return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
     }
 
+    @Override
+    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+        return new LayoutParams(p);
+    }
+
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof LayoutParams;
+    }
+
     public void addFullScreenPage(View page) {
         LayoutParams lp = generateDefaultLayoutParams();
         lp.isFullScreenPage = true;
@@ -841,11 +855,9 @@
         // Update the viewport offsets
         mViewport.offset(offsetX,  offsetY);
 
-        final boolean isRtl = isLayoutRtl();
-
-        final int startIndex = isRtl ? childCount - 1 : 0;
-        final int endIndex = isRtl ? -1 : childCount;
-        final int delta = isRtl ? -1 : 1;
+        final int startIndex = mIsRtl ? childCount - 1 : 0;
+        final int endIndex = mIsRtl ? -1 : childCount;
+        final int delta = mIsRtl ? -1 : 1;
 
         int verticalPadding = getPaddingTop() + getPaddingBottom();
 
@@ -897,7 +909,8 @@
                     pageGap = getPaddingRight();
                 }
 
-                childLeft += childWidth + pageGap;
+                childLeft += childWidth + pageGap
+                        + (lp.isFullScreenPage ? 0 : (getPaddingLeft() + getPaddingRight()));
             }
         }
 
@@ -948,7 +961,7 @@
     private void updateMaxScrollX() {
         int childCount = getChildCount();
         if (childCount > 0) {
-            final int index = isLayoutRtl() ? 0 : childCount - 1;
+            final int index = mIsRtl ? 0 : childCount - 1;
             mMaxScrollX = getScrollForPage(index);
         } else {
             mMaxScrollX = 0;
@@ -1258,7 +1271,7 @@
      * Return true if a tap at (x, y) should trigger a flip to the previous page.
      */
     protected boolean hitsPreviousPage(float x, float y) {
-        if (isLayoutRtl()) {
+        if (mIsRtl) {
             return (x > (getViewportOffsetX() + getViewportWidth() -
                     getPaddingRight() - mPageSpacing));
         }
@@ -1269,7 +1282,7 @@
      * Return true if a tap at (x, y) should trigger a flip to the next page.
      */
     protected boolean hitsNextPage(float x, float y) {
-        if (isLayoutRtl()) {
+        if (mIsRtl) {
             return (x < getViewportOffsetX() + getPaddingLeft() + mPageSpacing);
         }
         return  (x > (getViewportOffsetX() + getViewportWidth() -
@@ -1459,7 +1472,7 @@
         final int totalDistance;
 
         int adjacentPage = page + 1;
-        if ((delta < 0 && !isLayoutRtl()) || (delta > 0 && isLayoutRtl())) {
+        if ((delta < 0 && !mIsRtl) || (delta > 0 && mIsRtl)) {
             adjacentPage = page - 1;
         }
 
@@ -1494,7 +1507,7 @@
             int scrollOffset = 0;
             LayoutParams lp = (LayoutParams) child.getLayoutParams();
             if (!lp.isFullScreenPage) {
-                scrollOffset = isLayoutRtl() ? getPaddingRight() : getPaddingLeft();
+                scrollOffset = mIsRtl ? getPaddingRight() : getPaddingLeft();
             }
 
             int baselineX = mPageScrolls[index] + scrollOffset + getViewportOffsetX();
@@ -1571,7 +1584,7 @@
 
     void updateFreescrollBounds() {
         getFreeScrollPageRange(mTempVisiblePagesRange);
-        if (isLayoutRtl()) {
+        if (mIsRtl) {
             mFreeScrollMinScrollX = getScrollForPage(mTempVisiblePagesRange[1]);
             mFreeScrollMaxScrollX = getScrollForPage(mTempVisiblePagesRange[0]);
         } else {
@@ -1810,9 +1823,8 @@
                     // We give flings precedence over large moves, which is why we short-circuit our
                     // test for a large move if a fling has been registered. That is, a large
                     // move to the left and fling to the right will register as a fling to the right.
-                    final boolean isRtl = isLayoutRtl();
-                    boolean isDeltaXLeft = isRtl ? deltaX > 0 : deltaX < 0;
-                    boolean isVelocityXLeft = isRtl ? velocityX > 0 : velocityX < 0;
+                    boolean isDeltaXLeft = mIsRtl ? deltaX > 0 : deltaX < 0;
+                    boolean isVelocityXLeft = mIsRtl ? velocityX > 0 : velocityX < 0;
                     if (((isSignificantMove && !isDeltaXLeft && !isFling) ||
                             (isFling && !isVelocityXLeft)) && mCurrentPage > 0) {
                         finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
@@ -1936,7 +1948,7 @@
                         hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
                     }
                     if (hscroll != 0 || vscroll != 0) {
-                        boolean isForwardScroll = isLayoutRtl() ? (hscroll < 0 || vscroll < 0)
+                        boolean isForwardScroll = mIsRtl ? (hscroll < 0 || vscroll < 0)
                                                          : (hscroll > 0 || vscroll > 0);
                         if (isForwardScroll) {
                             scrollRight();
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 56c8b39..490ed6a 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -170,11 +170,7 @@
     }
 
     public boolean invertLayoutHorizontally() {
-        return mInvertIfRtl && isLayoutRtl();
-    }
-
-    public boolean isLayoutRtl() {
-        return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
+        return mInvertIfRtl && Utilities.isRtl(getResources());
     }
 
     @Override
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 8be4872..a9a8216 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -127,7 +127,7 @@
     /**
      * Refer {@link AppInfo#firstInstallTime}.
      */
-    long firstInstallTime;
+    public long firstInstallTime;
 
     /**
      * TODO move this to {@link status}
diff --git a/src/com/android/launcher3/SmoothPagedView.java b/src/com/android/launcher3/SmoothPagedView.java
index 4e331aa..0f9b23c 100644
--- a/src/com/android/launcher3/SmoothPagedView.java
+++ b/src/com/android/launcher3/SmoothPagedView.java
@@ -152,7 +152,7 @@
     }
 
     @Override
-    protected void snapToPage(int whichPage) {
+    public void snapToPage(int whichPage) {
        if (mScrollMode == X_LARGE_MODE) {
            super.snapToPage(whichPage);
        } else {
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index c351135..0fc8f32 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -1,5 +1,6 @@
 package com.android.launcher3;
 
+import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.Build;
@@ -7,7 +8,6 @@
 import android.os.UserManager;
 import android.util.AttributeSet;
 import android.util.Pair;
-
 import com.android.launcher3.R;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.util.Thunk;
@@ -36,6 +36,7 @@
         return supportsDrop(getContext(), info);
     }
 
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
     public static boolean supportsDrop(Context context, Object info) {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
             UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 2981747..6734fdc 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -627,8 +627,18 @@
      * Non-breaking whitespaces are also removed.
      */
     public static String trim(CharSequence s) {
+        if (s == null) {
+            return null;
+        }
+
         // Just strip any sequence of whitespace or java space characters from the beginning and end
         Matcher m = sTrimPattern.matcher(s);
         return m.replaceAll("$1");
     }
+
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+    public static boolean isRtl(Resources res) {
+        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) &&
+                (res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
+    }
 }
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 93bfeaf..8459673 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -88,7 +88,7 @@
      * @return a request id which can be used to cancel the request.
      */
     public PreviewLoadRequest getPreview(final Object o, int previewWidth, int previewHeight,
-            WidgetCell caller, Bitmap[] immediateResult) {
+            WidgetCell caller) {
         String size = previewWidth + "x" + previewHeight;
         WidgetCacheKey key = getObjectKey(o, size);
 
@@ -444,7 +444,7 @@
             } catch (Resources.NotFoundException e) { }
             c.setBitmap(null);
         }
-        return mManager.getBadgeBitmap(info, preview);
+        return mManager.getBadgeBitmap(info, preview, Math.min(preview.getHeight(), previewHeight));
     }
 
     private Bitmap generateShortcutPreview(
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 4004b1d..8c1c7d6 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -21,6 +21,7 @@
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
+import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
 import android.app.WallpaperManager;
 import android.appwidget.AppWidgetHostView;
@@ -60,10 +61,11 @@
 import com.android.launcher3.FolderIcon.FolderRingAnimator;
 import com.android.launcher3.Launcher.CustomContentCallbacks;
 import com.android.launcher3.Launcher.LauncherOverlay;
-import com.android.launcher3.LauncherAccessibilityDelegate.AccessibilityDragSource;
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.UninstallDropTarget.UninstallSource;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.Thunk;
@@ -495,7 +497,7 @@
     /**
      * @return The open folder on the current screen, or null if there is none
      */
-    Folder getOpenFolder() {
+    public Folder getOpenFolder() {
         DragLayer dragLayer = mLauncher.getDragLayer();
         int count = dragLayer.getChildCount();
         for (int i = 0; i < count; i++) {
@@ -553,8 +555,10 @@
             throw new RuntimeException("Screen id " + screenId + " already exists!");
         }
 
-        CellLayout newScreen = (CellLayout)
-                mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
+        // Inflate the cell layout, but do not add it automatically so that we can get the newly
+        // created CellLayout.
+        CellLayout newScreen = (CellLayout) mLauncher.getLayoutInflater().inflate(
+                        R.layout.workspace_screen, this, false /* attachToRoot */);
 
         newScreen.setOnLongClickListener(mLongClickListener);
         newScreen.setOnClickListener(mLauncher);
@@ -573,7 +577,7 @@
 
     public void createCustomContentContainer() {
         CellLayout customScreen = (CellLayout)
-                mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
+                mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, this, false);
         customScreen.disableBackground();
         customScreen.disableDragTarget();
 
@@ -856,7 +860,7 @@
         return -1;
     }
 
-    ArrayList<Long> getScreenOrder() {
+    public ArrayList<Long> getScreenOrder() {
         return mScreenOrder;
     }
 
@@ -1033,6 +1037,7 @@
      * listener via setOnInterceptTouchEventListener(). This allows us to tell the CellLayout
      * that it should intercept touch events, which is not something that is normally supported.
      */
+    @SuppressLint("ClickableViewAccessibility")
     @Override
     public boolean onTouch(View v, MotionEvent event) {
         return (workspaceInModalState() || !isFinishedSwitchingState())
@@ -1136,7 +1141,7 @@
         boolean passRightSwipesToCustomContent =
                 (mTouchDownTime - mCustomContentShowTime) > CUSTOM_CONTENT_GESTURE_DELAY;
 
-        boolean swipeInIgnoreDirection = isLayoutRtl() ? deltaX < 0 : deltaX > 0;
+        boolean swipeInIgnoreDirection = mIsRtl ? deltaX < 0 : deltaX > 0;
         boolean onCustomContentScreen =
                 getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID;
         if (swipeInIgnoreDirection && onCustomContentScreen && passRightSwipesToCustomContent) {
@@ -1243,15 +1248,14 @@
 
     @Override
     protected void overScroll(float amount) {
-        boolean isRtl = isLayoutRtl();
-        boolean shouldOverScroll = (amount <= 0 && (!hasCustomContent() || isRtl)) ||
-                (amount >= 0 && (!hasCustomContent() || !isRtl));
+        boolean shouldOverScroll = (amount <= 0 && (!hasCustomContent() || mIsRtl)) ||
+                (amount >= 0 && (!hasCustomContent() || !mIsRtl));
 
         boolean shouldScrollOverlay = mLauncherOverlay != null &&
-                ((amount <= 0 && !isRtl) || (amount >= 0 && isRtl));
+                ((amount <= 0 && !mIsRtl) || (amount >= 0 && mIsRtl));
 
         boolean shouldZeroOverlay = mLauncherOverlay != null && mLastOverlaySroll != 0 &&
-                ((amount >= 0 && !isRtl) || (amount <= 0 && isRtl));
+                ((amount >= 0 && !mIsRtl) || (amount <= 0 && mIsRtl));
 
         if (shouldScrollOverlay) {
             if (!mStartedSendingScrollEvents && mScrollInteractionBegan) {
@@ -1265,7 +1269,7 @@
             int progress = (int) Math.abs((f * 100));
 
             mLastOverlaySroll = progress;
-            mLauncherOverlay.onScrollChange(progress, isRtl);
+            mLauncherOverlay.onScrollChange(progress, mIsRtl);
         } else if (shouldOverScroll) {
             dampedOverScroll(amount);
             mOverScrollEffect = acceleratedOverFactor(amount);
@@ -1274,7 +1278,7 @@
         }
 
         if (shouldZeroOverlay) {
-            mLauncherOverlay.onScrollChange(0, isRtl);
+            mLauncherOverlay.onScrollChange(0, mIsRtl);
         }
     }
 
@@ -1409,7 +1413,7 @@
             mNumPagesForWallpaperParallax = parallaxPageSpan;
 
             if (getChildCount() <= 1) {
-                if (isLayoutRtl()) {
+                if (mIsRtl) {
                     return 1 - 1.0f/mNumPagesForWallpaperParallax;
                 }
                 return 0;
@@ -1420,7 +1424,7 @@
             int firstIndex = numCustomPages();
             // Exclude the last extra empty screen (if we have > MIN_PARALLAX_PAGE_SPAN pages)
             int lastIndex = getChildCount() - 1 - emptyExtraPages;
-            if (isLayoutRtl()) {
+            if (mIsRtl) {
                 int temp = firstIndex;
                 firstIndex = lastIndex;
                 lastIndex = temp;
@@ -1441,7 +1445,7 @@
                 // On RTL devices, push the wallpaper offset to the right if we don't have enough
                 // pages (ie if numScrollingPages < MIN_PARALLAX_PAGE_SPAN)
                 if (!mWallpaperIsLiveWallpaper && numScrollingPages < MIN_PARALLAX_PAGE_SPAN
-                        && isLayoutRtl()) {
+                        && mIsRtl) {
                     return offset * (parallaxPageSpan - numScrollingPages + 1) / parallaxPageSpan;
                 }
                 return offset * (numScrollingPages - 1) / parallaxPageSpan;
@@ -1648,7 +1652,7 @@
             translationX = scrollRange - scrollDelta;
             progress = (scrollRange - scrollDelta) / scrollRange;
 
-            if (isLayoutRtl()) {
+            if (mIsRtl) {
                 translationX = Math.min(0, translationX);
             } else {
                 translationX = Math.max(0, translationX);
@@ -1702,7 +1706,6 @@
 
     @Override
     protected void screenScrolled(int screenCenter) {
-        final boolean isRtl = isLayoutRtl();
         super.screenScrolled(screenCenter);
 
         updatePageAlphaValues(screenCenter);
@@ -1717,7 +1720,7 @@
             final int upperIndex = getChildCount() - 1;
 
             final boolean isLeftPage = mOverScrollX < 0;
-            index = (!isRtl && isLeftPage) || (isRtl && !isLeftPage) ? lowerIndex : upperIndex;
+            index = (!mIsRtl && isLeftPage) || (mIsRtl && !isLeftPage) ? lowerIndex : upperIndex;
 
             CellLayout cl = (CellLayout) getChildAt(index);
             float effect = Math.abs(mOverScrollEffect);
diff --git a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
index 0f17241..78accf7 100644
--- a/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/DragAndDropAccessibilityDelegate.java
@@ -26,7 +26,6 @@
 import android.view.accessibility.AccessibilityEvent;
 
 import com.android.launcher3.CellLayout;
-import com.android.launcher3.LauncherAccessibilityDelegate;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 
diff --git a/src/com/android/launcher3/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
similarity index 94%
rename from src/com/android/launcher3/LauncherAccessibilityDelegate.java
rename to src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 3992e63..eeec8c5 100644
--- a/src/com/android/launcher3/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -1,4 +1,4 @@
-package com.android.launcher3;
+package com.android.launcher3.accessibility;
 
 import android.annotation.TargetApi;
 import android.app.AlertDialog;
@@ -16,6 +16,24 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.AppWidgetResizeFrame;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DeleteDropTarget;
+import com.android.launcher3.Folder;
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.InfoDropTarget;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppWidgetHostView;
+import com.android.launcher3.LauncherAppWidgetInfo;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.R;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.UninstallDropTarget;
+import com.android.launcher3.Workspace;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
diff --git a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
index d3f5230..c5b52de 100644
--- a/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/OverviewScreenAccessibilityDelegate.java
@@ -16,17 +16,20 @@
 
 package com.android.launcher3.accessibility;
 
+import android.annotation.TargetApi;
 import android.content.Context;
+import android.os.Build;
 import android.os.Bundle;
 import android.util.SparseArray;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
-
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.Workspace;
 
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class OverviewScreenAccessibilityDelegate extends AccessibilityDelegate {
 
     private static final int MOVE_BACKWARD = R.id.action_move_screen_backwards;
@@ -39,7 +42,7 @@
         mWorkspace = workspace;
 
         Context context = mWorkspace.getContext();
-        boolean isRtl = mWorkspace.isLayoutRtl();
+        boolean isRtl = Utilities.isRtl(context.getResources());
         mActions.put(MOVE_BACKWARD, new AccessibilityAction(MOVE_BACKWARD,
                 context.getText(isRtl ? R.string.action_move_screen_right :
                     R.string.action_move_screen_left)));
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index 6f89d0e..80ddc13 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -23,8 +23,7 @@
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAccessibilityDelegate;
-import com.android.launcher3.LauncherAccessibilityDelegate.DragType;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
 
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index e41a66f..7aa36d4 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -78,6 +78,7 @@
 
     public abstract Drawable loadIcon(LauncherAppWidgetProviderInfo info, IconCache cache);
 
-    public abstract Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap);
+    public abstract Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap,
+            int imageHeight);
 
 }
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
index 967b53b..a64c705 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.compat;
 
+import android.annotation.TargetApi;
 import android.app.Activity;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetManager;
@@ -49,6 +50,7 @@
         return Utilities.trim(info.label);
     }
 
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     @Override
     public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info,
             Bundle options) {
@@ -85,7 +87,7 @@
     }
 
     @Override
-    public Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap) {
+    public Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap, int imageHeight) {
         return bitmap;
     }
 }
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
index 6c3e092..96ace84 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -108,7 +108,7 @@
     }
 
     @Override
-    public Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap) {
+    public Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap, int imageHeight) {
         if (info.isCustomWidget || info.getProfile().equals(android.os.Process.myUserHandle())) {
             return bitmap;
         }
@@ -117,9 +117,10 @@
         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 int badgeMinTop = res.getDimensionPixelSize(R.dimen.profile_badge_minimum_top);
         final Rect badgeLocation = new Rect(0, 0, badgeSize, badgeSize);
 
-        final int top = bitmap.getHeight() - badgeSize - badgeMargin;
+        final int top = Math.max(imageHeight - badgeSize - badgeMargin, badgeMinTop);
         if (res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
             badgeLocation.offset(badgeMargin, top);
         } else {
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
index b52cf1d..4448758 100644
--- a/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
@@ -16,12 +16,14 @@
 
 package com.android.launcher3.compat;
 
+import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherActivityInfo;
 import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
+import android.os.Build;
 
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class LauncherActivityInfoCompatVL extends LauncherActivityInfoCompat {
     private LauncherActivityInfo mLauncherActivityInfo;
 
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index e0d28b5..c862ffc 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.compat;
 
+import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -32,6 +33,7 @@
 import java.util.List;
 import java.util.Map;
 
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class LauncherAppsCompatVL extends LauncherAppsCompat {
 
     private LauncherApps mLauncherApps;
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 395d5f9..3ad5101 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -16,10 +16,12 @@
 
 package com.android.launcher3.compat;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionCallback;
 import android.content.pm.PackageInstaller.SessionInfo;
+import android.os.Build;
 import android.os.Handler;
 import android.util.SparseArray;
 
@@ -30,6 +32,7 @@
 
 import java.util.HashMap;
 
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class PackageInstallerCompatVL extends PackageInstallerCompat {
 
     @Thunk final SparseArray<String> mActiveSessions = new SparseArray<>();
diff --git a/src/com/android/launcher3/compat/UserHandleCompat.java b/src/com/android/launcher3/compat/UserHandleCompat.java
index 2ae6731..d8e60b8 100644
--- a/src/com/android/launcher3/compat/UserHandleCompat.java
+++ b/src/com/android/launcher3/compat/UserHandleCompat.java
@@ -16,10 +16,10 @@
 
 package com.android.launcher3.compat;
 
+import android.annotation.TargetApi;
 import android.content.Intent;
 import android.os.Build;
 import android.os.UserHandle;
-
 import com.android.launcher3.Utilities;
 
 public class UserHandleCompat {
@@ -32,6 +32,7 @@
     private UserHandleCompat() {
     }
 
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     public static UserHandleCompat myUserHandle() {
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
             return new UserHandleCompat(android.os.Process.myUserHandle());
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV17.java b/src/com/android/launcher3/compat/UserManagerCompatV17.java
index 055359a..c42c00c 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatV17.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatV17.java
@@ -16,14 +16,12 @@
 
 package com.android.launcher3.compat;
 
+import android.annotation.TargetApi;
 import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
+import android.os.Build;
 import android.os.UserManager;
 
-import java.util.ArrayList;
-import java.util.List;
-
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
 public class UserManagerCompatV17 extends UserManagerCompatV16 {
     protected UserManager mUserManager;
 
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index 884d6fe..f6434c5 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -17,18 +17,19 @@
 
 package com.android.launcher3.compat;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.UserHandle;
-
 import com.android.launcher3.LauncherAppState;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
 public class UserManagerCompatVL extends UserManagerCompatV17 {
     private static final String USER_CREATION_TIME_KEY = "user_creation_time_";
 
diff --git a/src/com/android/launcher3/model/AppNameComparator.java b/src/com/android/launcher3/model/AppNameComparator.java
new file mode 100644
index 0000000..706f751
--- /dev/null
+++ b/src/com/android/launcher3/model/AppNameComparator.java
@@ -0,0 +1,105 @@
+package com.android.launcher3.model;
+
+import android.content.Context;
+
+import com.android.launcher3.AppInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+import java.text.Collator;
+import java.util.Comparator;
+import java.util.HashMap;
+
+/**
+ * Class to manage access to an app name comparator.
+ * <p>
+ * Used to sort application name in all apps view and widget tray view.
+ */
+public class AppNameComparator {
+    private final UserManagerCompat mUserManager;
+    private final Collator mCollator;
+    private final Comparator<ItemInfo> mAppInfoComparator;
+    private final Comparator<String> mSectionNameComparator;
+    private HashMap<UserHandleCompat, Long> mUserSerialCache = new HashMap<>();
+
+    public AppNameComparator(Context context) {
+        mCollator = Collator.getInstance();
+        mUserManager = UserManagerCompat.getInstance(context);
+        mAppInfoComparator = new Comparator<ItemInfo>() {
+
+            public final int compare(ItemInfo a, ItemInfo b) {
+                // Order by the title in the current locale
+                int result = compareTitles(a.title.toString(), b.title.toString());
+                if (result == 0 && a instanceof AppInfo && b instanceof AppInfo) {
+                    AppInfo aAppInfo = (AppInfo) a;
+                    AppInfo bAppInfo = (AppInfo) b;
+                    // If two apps have the same title, then order by the component name
+                    result = aAppInfo.componentName.compareTo(bAppInfo.componentName);
+                    if (result == 0) {
+                        // If the two apps are the same component, then prioritize by the order that
+                        // the app user was created (prioritizing the main user's apps)
+                        if (UserHandleCompat.myUserHandle().equals(a.user)) {
+                            return -1;
+                        } else {
+                            Long aUserSerial = getAndCacheUserSerial(a.user);
+                            Long bUserSerial = getAndCacheUserSerial(b.user);
+                            return aUserSerial.compareTo(bUserSerial);
+                        }
+                    }
+                }
+                return result;
+            }
+        };
+        mSectionNameComparator = new Comparator<String>() {
+            @Override
+            public int compare(String o1, String o2) {
+                return compareTitles(o1, o2);
+            }
+        };
+    }
+
+    /**
+     * Returns a locale-aware comparator that will alphabetically order a list of applications.
+     */
+    public Comparator<ItemInfo> getAppInfoComparator() {
+        // Clear the user serial cache so that we get serials as needed in the comparator
+        mUserSerialCache.clear();
+        return mAppInfoComparator;
+    }
+
+    /**
+     * Returns a locale-aware comparator that will alphabetically order a list of section names.
+     */
+    public Comparator<String> getSectionNameComparator() {
+        return mSectionNameComparator;
+    }
+
+    /**
+     * Compares two titles with the same return value semantics as Comparator.
+     */
+    private int compareTitles(String titleA, String titleB) {
+        // Ensure that we de-prioritize any titles that don't start with a linguistic letter or digit
+        boolean aStartsWithLetter = Character.isLetterOrDigit(titleA.codePointAt(0));
+        boolean bStartsWithLetter = Character.isLetterOrDigit(titleB.codePointAt(0));
+        if (aStartsWithLetter && !bStartsWithLetter) {
+            return -1;
+        } else if (!aStartsWithLetter && bStartsWithLetter) {
+            return 1;
+        }
+
+        // Order by the title in the current locale
+        return mCollator.compare(titleA, titleB);
+    }
+
+    /**
+     * Returns the user serial for this user, using a cached serial if possible.
+     */
+    private Long getAndCacheUserSerial(UserHandleCompat user) {
+        Long userSerial = mUserSerialCache.get(user);
+        if (userSerial == null) {
+            userSerial = mUserManager.getSerialNumberForUser(user);
+            mUserSerialCache.put(user, userSerial);
+        }
+        return userSerial;
+    }
+}
diff --git a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
new file mode 100644
index 0000000..61e8952
--- /dev/null
+++ b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
@@ -0,0 +1,68 @@
+package com.android.launcher3.model;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+
+import java.text.Collator;
+import java.util.Comparator;
+import java.util.HashMap;
+
+public class WidgetsAndShortcutNameComparator implements Comparator<Object> {
+    private final AppWidgetManagerCompat mManager;
+    private final PackageManager mPackageManager;
+    private final HashMap<Object, String> mLabelCache;
+    private final Collator mCollator;
+    private final UserHandleCompat mMainHandle;
+
+    public WidgetsAndShortcutNameComparator(Context context) {
+        mManager = AppWidgetManagerCompat.getInstance(context);
+        mPackageManager = context.getPackageManager();
+        mLabelCache = new HashMap<Object, String>();
+        mCollator = Collator.getInstance();
+        mMainHandle = UserHandleCompat.myUserHandle();
+    }
+
+    @Override
+    public final int compare(Object a, Object b) {
+        String labelA, labelB;
+        if (mLabelCache.containsKey(a)) {
+            labelA = mLabelCache.get(a);
+        } else {
+            labelA = (a instanceof LauncherAppWidgetProviderInfo)
+                    ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) a))
+                    : Utilities.trim(((ResolveInfo) a).loadLabel(mPackageManager));
+            mLabelCache.put(a, labelA);
+        }
+        if (mLabelCache.containsKey(b)) {
+            labelB = mLabelCache.get(b);
+        } else {
+            labelB = (b instanceof LauncherAppWidgetProviderInfo)
+                    ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) b))
+                    : Utilities.trim(((ResolveInfo) b).loadLabel(mPackageManager));
+            mLabelCache.put(b, labelB);
+        }
+
+        // Currently, there is no work profile shortcuts, hence only considering the widget cases.
+
+        boolean aWorkProfile = (a instanceof LauncherAppWidgetProviderInfo) &&
+                !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) a));
+        boolean bWorkProfile = (b instanceof LauncherAppWidgetProviderInfo) &&
+                !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) b));
+
+        // Independent of how the labels compare, if only one of the two widget info belongs to
+        // work profile, put that one in the back.
+        if (aWorkProfile && !bWorkProfile) {
+            return 1;
+        }
+        if (!aWorkProfile && bWorkProfile) {
+            return -1;
+        }
+        return mCollator.compare(labelA, labelB);
+    }
+};
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index cefa71c..94dc47f 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -23,8 +23,10 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Handles addition of app shortcuts for managed profiles.
@@ -62,8 +64,8 @@
     private final long mUserCreationTime;
     private final String mPackageSetKey;
 
-    private ArrayList<ItemInfo> mHomescreenApps;
-    private ArrayList<ItemInfo> mWorkFolderApps;
+    private ArrayList<ShortcutInfo> mHomescreenApps;
+    private ArrayList<ShortcutInfo> mWorkFolderApps;
 
     private ManagedProfileHeuristic(Context context, UserHandleCompat user) {
         mContext = context;
@@ -84,9 +86,12 @@
      * workfolder.
      */
     public void processUserApps(List<LauncherActivityInfoCompat> apps) {
-        mHomescreenApps = new ArrayList<ItemInfo>();
-        mWorkFolderApps = new ArrayList<ItemInfo>();
-        HashSet<String> packageSet = getPackageSet();
+        mHomescreenApps = new ArrayList<>();
+        mWorkFolderApps = new ArrayList<>();
+
+        HashSet<String> packageSet = new HashSet<>();
+        final boolean userAppsExisted = getUserApps(packageSet);
+
         boolean newPackageAdded = false;
 
         for (LauncherActivityInfoCompat info : apps) {
@@ -107,12 +112,15 @@
 
         if (newPackageAdded) {
             mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply();
-            finalizeAdditions();
+            // Do not add shortcuts on the homescreen for the first time. This prevents the launcher
+            // getting filled with the managed user apps, when it start with a fresh DB (or after
+            // a very long time).
+            finalizeAdditions(userAppsExisted);
         }
     }
 
     private void markForAddition(LauncherActivityInfoCompat info, long installTime) {
-        ArrayList<ItemInfo> targetList =
+        ArrayList<ShortcutInfo> targetList =
                 (installTime <= mUserCreationTime + AUTO_ADD_TO_FOLDER_DURATION) ?
                         mWorkFolderApps : mHomescreenApps;
         targetList.add(ShortcutInfo.fromActivityInfo(info, mContext));
@@ -125,6 +133,13 @@
         if (mWorkFolderApps.isEmpty()) {
             return;
         }
+        Collections.sort(mWorkFolderApps, new Comparator<ShortcutInfo>() {
+
+            @Override
+            public int compare(ShortcutInfo lhs, ShortcutInfo rhs) {
+                return Long.compare(lhs.firstInstallTime, rhs.firstInstallTime);
+            }
+        });
 
         // Try to get a work folder.
         String folderIdKey = USER_FOLDER_ID_PREFIX + mUserSerial;
@@ -139,14 +154,14 @@
             }
             saveWorkFolderShortcuts(folderId, workFolder.contents.size());
 
-            final ArrayList<ItemInfo> shortcuts = mWorkFolderApps;
+            final ArrayList<ShortcutInfo> shortcuts = mWorkFolderApps;
             // FolderInfo could already be bound. We need to add shortcuts on the UI thread.
             new MainThreadExecutor().execute(new Runnable() {
 
                 @Override
                 public void run() {
-                    for (ItemInfo info : shortcuts) {
-                        workFolder.add((ShortcutInfo) info);
+                    for (ShortcutInfo info : shortcuts) {
+                        workFolder.add(info);
                     }
                 }
             });
@@ -157,8 +172,8 @@
             workFolder.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null);
 
             // Add all shortcuts before adding it to the UI, as an empty folder might get deleted.
-            for (ItemInfo info : mWorkFolderApps) {
-                workFolder.add((ShortcutInfo) info);
+            for (ShortcutInfo info : mWorkFolderApps) {
+                workFolder.add(info);
             }
 
             // Add the item to home screen and DB. This also generates an item id synchronously.
@@ -184,10 +199,10 @@
     /**
      * Adds and binds all shortcuts marked for addition.
      */
-    private void finalizeAdditions() {
+    private void finalizeAdditions(boolean addHomeScreenShortcuts) {
         finalizeWorkFolder();
 
-        if (!mHomescreenApps.isEmpty()) {
+        if (addHomeScreenShortcuts && !mHomescreenApps.isEmpty()) {
             mModel.addAndBindAddedWorkspaceItems(mContext, mHomescreenApps);
         }
     }
@@ -196,9 +211,12 @@
      * Updates the list of installed apps and adds any new icons on homescreen or work folder.
      */
     public void processPackageAdd(String[] packages) {
-        mHomescreenApps = new ArrayList<ItemInfo>();
-        mWorkFolderApps = new ArrayList<ItemInfo>();
-        HashSet<String> packageSet = getPackageSet();
+        mHomescreenApps = new ArrayList<>();
+        mWorkFolderApps = new ArrayList<>();
+
+        HashSet<String> packageSet = new HashSet<>();
+        final boolean userAppsExisted = getUserApps(packageSet);
+
         boolean newPackageAdded = false;
         long installTime = System.currentTimeMillis();
         LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
@@ -218,7 +236,7 @@
 
         if (newPackageAdded) {
             mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply();
-            finalizeAdditions();
+            finalizeAdditions(userAppsExisted);
         }
     }
 
@@ -226,7 +244,8 @@
      * Updates the list of installed packages for the user.
      */
     public void processPackageRemoved(String[] packages) {
-        HashSet<String> packageSet = getPackageSet();
+        HashSet<String> packageSet = new HashSet<String>();
+        getUserApps(packageSet);
         boolean packageRemoved = false;
 
         for (String packageName : packages) {
@@ -240,9 +259,18 @@
         }
     }
 
-    @SuppressWarnings("unchecked")
-    private HashSet<String> getPackageSet() {
-        return new HashSet<String>(mPrefs.getStringSet(mPackageSetKey, Collections.EMPTY_SET));
+    /**
+     * Reads the list of user apps which have already been processed.
+     * @return false if the list didn't exist, true otherwise
+     */
+    private boolean getUserApps(HashSet<String> outExistingApps) {
+        Set<String> userApps = mPrefs.getStringSet(mPackageSetKey, null);
+        if (userApps == null) {
+            return false;
+        } else {
+            outExistingApps.addAll(userApps);
+            return true;
+        }
     }
 
     /**
@@ -274,4 +302,30 @@
         keysOut.add(INSTALLED_PACKAGES_PREFIX + userSerial);
         keysOut.add(USER_FOLDER_ID_PREFIX + userSerial);
     }
+
+    /**
+     * For each user, if a work folder has not been created, mark it such that the folder will
+     * never get created.
+     */
+    public static void markExistingUsersForNoFolderCreation(Context context) {
+        UserManagerCompat userManager = UserManagerCompat.getInstance(context);
+        UserHandleCompat myUser = UserHandleCompat.myUserHandle();
+
+        SharedPreferences prefs = null;
+        for (UserHandleCompat user : userManager.getUserProfiles()) {
+            if (myUser.equals(user)) {
+                continue;
+            }
+
+            if (prefs == null) {
+                prefs = context.getSharedPreferences(
+                        LauncherFiles.MANAGED_USER_PREFERENCES_KEY,
+                        Context.MODE_PRIVATE);
+            }
+            String folderIdKey = USER_FOLDER_ID_PREFIX + userManager.getSerialNumberForUser(user);
+            if (!prefs.contains(folderIdKey)) {
+                prefs.edit().putLong(folderIdKey, ItemInfo.NO_ID).apply();
+            }
+        }
+    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 7ca4df9..dcaf1f2 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -21,17 +21,14 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnLayoutChangeListener;
-import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
@@ -41,7 +38,13 @@
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 
 /**
- * Represents the individual cell of the widget inside the widget tray.
+ * Represents the individual cell of the widget inside the widget tray. The preview is drawn
+ * horizontally centered, and scaled down if needed.
+ *
+ * This view does not support padding. Since the image is scaled down to fit the view, padding will
+ * further decrease the scaling factor. Drag-n-drop uses the view bounds for showing a smooth
+ * transition from the view to drag view, so when adding padding support, DnD would need to
+ * consider the appropriate scaling factor.
  */
 public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
 
@@ -59,13 +62,11 @@
     private int mPresetPreviewSize;
     int cellSize;
 
-    private ImageView mWidgetImage;
+    private WidgetImageView mWidgetImage;
     private TextView mWidgetName;
     private TextView mWidgetDims;
-    private final Rect mOriginalImagePadding = new Rect();
 
     private String mDimensionsFormatString;
-    private boolean mIsAppWidget;
     private Object mInfo;
 
     private WidgetPreviewLoader mWidgetPreviewLoader;
@@ -101,12 +102,7 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        mWidgetImage = (ImageView) findViewById(R.id.widget_preview);
-        mOriginalImagePadding.left = mWidgetImage.getPaddingLeft();
-        mOriginalImagePadding.top = mWidgetImage.getPaddingTop();
-        mOriginalImagePadding.right = mWidgetImage.getPaddingRight();
-        mOriginalImagePadding.bottom = mWidgetImage.getPaddingBottom();
-
+        mWidgetImage = (WidgetImageView) findViewById(R.id.widget_preview);
         mWidgetName = ((TextView) findViewById(R.id.widget_name));
         mWidgetDims = ((TextView) findViewById(R.id.widget_dims));
     }
@@ -118,7 +114,8 @@
         if (DEBUG) {
             Log.d(TAG, "reset called on:" + mWidgetName.getText());
         }
-        mWidgetImage.setImageDrawable(null);
+        mWidgetImage.animate().cancel();
+        mWidgetImage.setBitmap(null);
         mWidgetName.setText(null);
         mWidgetDims.setText(null);
 
@@ -132,19 +129,15 @@
      * Apply the widget provider info to the view.
      */
     public void applyFromAppWidgetProviderInfo(LauncherAppWidgetProviderInfo info,
-            int maxWidth, WidgetPreviewLoader loader) {
+            WidgetPreviewLoader loader) {
         LauncherAppState app = LauncherAppState.getInstance();
         DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
 
-        mIsAppWidget = true;
         mInfo = info;
-        if (maxWidth > -1) {
-            mWidgetImage.setMaxWidth(maxWidth);
-        }
         // TODO(hyunyoungs): setup a cache for these labels.
         mWidgetName.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
-        int hSpan = Math.min(info.spanX, (int) grid.numColumns);
-        int vSpan = Math.min(info.spanY, (int) grid.numRows);
+        int hSpan = Math.min(info.spanX, grid.numColumns);
+        int vSpan = Math.min(info.spanY, grid.numRows);
         mWidgetDims.setText(String.format(mDimensionsFormatString, hSpan, vSpan));
         mWidgetPreviewLoader = loader;
     }
@@ -154,7 +147,6 @@
      */
     public void applyFromResolveInfo(
             PackageManager pm, ResolveInfo info, WidgetPreviewLoader loader) {
-        mIsAppWidget = false;
         mInfo = info;
         CharSequence label = info.loadLabel(pm);
         mWidgetName.setText(label);
@@ -164,32 +156,17 @@
 
     public int[] getPreviewSize() {
         int[] maxSize = new int[2];
+
         maxSize[0] = mPresetPreviewSize;
         maxSize[1] = mPresetPreviewSize;
         return maxSize;
     }
 
     public void applyPreview(Bitmap bitmap) {
-        FastBitmapDrawable preview = new FastBitmapDrawable(bitmap);
-        if (DEBUG) {
-            Log.d(TAG, String.format("[tag=%s] applyPreview preview: %s",
-                    getTagToString(), preview));
-        }
-        if (preview != null) {
-            mWidgetImage.setImageDrawable(preview);
-            if (mIsAppWidget) {
-                // center horizontally
-                int[] imageSize = getPreviewSize();
-                int centerAmount = (imageSize[0] - preview.getIntrinsicWidth()) / 2;
-                mWidgetImage.setPadding(mOriginalImagePadding.left + centerAmount,
-                        mOriginalImagePadding.top,
-                        mOriginalImagePadding.right,
-                        mOriginalImagePadding.bottom);
-            }
+        if (bitmap != null) {
+            mWidgetImage.setBitmap(bitmap);
             mWidgetImage.setAlpha(0f);
             mWidgetImage.animate().alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
-            // TODO(hyunyoungs): figure out why this has to be called explicitly.
-            mWidgetImage.requestLayout();
         }
     }
 
@@ -202,17 +179,7 @@
             Log.d(TAG, String.format("[tag=%s] ensurePreview (%d, %d):",
                     getTagToString(), size[0], size[1]));
         }
-
-        if (size[0] <= 0 || size[1] <= 0) {
-            addOnLayoutChangeListener(this);
-            return;
-        }
-        Bitmap[] immediateResult = new Bitmap[1];
-        mActiveRequest = mWidgetPreviewLoader.getPreview(mInfo, size[0], size[1], this,
-                immediateResult);
-        if (immediateResult[0] != null) {
-            applyPreview(immediateResult[0]);
-        }
+        mActiveRequest = mWidgetPreviewLoader.getPreview(mInfo, size[0], size[1], this);
     }
 
     @Override
diff --git a/src/com/android/launcher3/widget/WidgetImageView.java b/src/com/android/launcher3/widget/WidgetImageView.java
index 75167bc..6f8fd89 100644
--- a/src/com/android/launcher3/widget/WidgetImageView.java
+++ b/src/com/android/launcher3/widget/WidgetImageView.java
@@ -17,32 +17,73 @@
 package com.android.launcher3.widget;
 
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
 import android.util.AttributeSet;
-import android.widget.ImageView;
+import android.view.View;
 
-public class WidgetImageView extends ImageView {
-    public boolean mAllowRequestLayout = true;
+/**
+ * View that draws a bitmap horizontally centered. If the image width is greater than the view
+ * width, the image is scaled down appropriately.
+ */
+public class WidgetImageView extends View {
+
+    private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+    private final RectF mDstRectF = new RectF();
+    private Bitmap mBitmap;
+
+    public WidgetImageView(Context context) {
+        super(context);
+    }
 
     public WidgetImageView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
-    public void requestLayout() {
-        if (mAllowRequestLayout) {
-            super.requestLayout();
-        }
+    public WidgetImageView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public void setBitmap(Bitmap bitmap) {
+        mBitmap = bitmap;
+        invalidate();
+    }
+
+    public Bitmap getBitmap() {
+        return mBitmap;
     }
 
     @Override
     protected void onDraw(Canvas canvas) {
-        canvas.save();
-        canvas.clipRect(getScrollX() + getPaddingLeft(),
-                getScrollY() + getPaddingTop(),
-                getScrollX() + getRight() - getLeft() - getPaddingRight(),
-                getScrollY() + getBottom() - getTop() - getPaddingBottom());
+        if (mBitmap != null) {
+            updateDstRectF();
+            canvas.drawBitmap(mBitmap, null, mDstRectF, mPaint);
+        }
+    }
 
-        super.onDraw(canvas);
-        canvas.restore();
+    private void updateDstRectF() {
+        if (mBitmap.getWidth() > getWidth()) {
+            float scale = ((float) getWidth()) / mBitmap.getWidth();
+            mDstRectF.set(0, 0, getWidth(), scale * mBitmap.getHeight());
+        } else {
+            mDstRectF.set(
+                    (getWidth() - mBitmap.getWidth()) * 0.5f,
+                    0,
+                    (getWidth() + mBitmap.getWidth()) * 0.5f,
+                    mBitmap.getHeight());
+        }
+    }
+
+    /**
+     * @return the bounds where the image was drawn.
+     */
+    public Rect getBitmapBounds() {
+        updateDstRectF();
+        Rect rect = new Rect();
+        mDstRectF.round(rect);
+        return rect;
     }
 }
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 00fb225..181c08a 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.support.v7.widget.LinearLayoutManager;
@@ -28,8 +27,8 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
-import android.widget.ImageView;
 import android.widget.Toast;
+
 import com.android.launcher3.BaseContainerView;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeleteDropTarget;
@@ -37,10 +36,8 @@
 import com.android.launcher3.DragController;
 import com.android.launcher3.DragSource;
 import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.Folder;
 import com.android.launcher3.IconCache;
-import com.android.launcher3.Insettable;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
@@ -222,20 +219,19 @@
 
     private boolean beginDraggingWidget(WidgetCell v) {
         // Get the widget preview as the drag representation
-        ImageView image = (ImageView) v.findViewById(R.id.widget_preview);
+        WidgetImageView image = (WidgetImageView) v.findViewById(R.id.widget_preview);
         PendingAddItemInfo createItemInfo = (PendingAddItemInfo) v.getTag();
 
         // If the ImageView doesn't have a drawable yet, the widget preview hasn't been loaded and
         // we abort the drag.
-        if (image.getDrawable() == null) {
+        if (image.getBitmap() == null) {
             return false;
         }
 
         // Compose the drag image
         Bitmap preview;
-        Bitmap outline;
         float scale = 1f;
-        Point previewPadding = null;
+        final Rect bounds = image.getBitmapBounds();
 
         if (createItemInfo instanceof PendingAddWidgetInfo) {
             // This can happen in some weird cases involving multi-touch. We can't start dragging
@@ -244,25 +240,25 @@
             PendingAddWidgetInfo createWidgetInfo = (PendingAddWidgetInfo) createItemInfo;
             int[] size = mLauncher.getWorkspace().estimateItemSize(createWidgetInfo, true);
 
-            FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable();
+            Bitmap icon = image.getBitmap();
             float minScale = 1.25f;
-            int maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]);
+            int maxWidth = Math.min((int) (icon.getWidth() * minScale), size[0]);
 
             int[] previewSizeBeforeScale = new int[1];
             preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info,
                     maxWidth, null, previewSizeBeforeScale);
-            // Compare the size of the drag preview to the preview in the AppsCustomize tray
-            int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0],
-                    v.getActualItemWidth());
-            scale = previewWidthInAppsCustomize / (float) preview.getWidth();
 
-            // The bitmap in the AppsCustomize tray is always the the same size, so there
-            // might be extra pixels around the preview itself - this accounts for that
-            if (previewWidthInAppsCustomize < previewDrawable.getIntrinsicWidth()) {
-                int padding =
-                        (previewDrawable.getIntrinsicWidth() - previewWidthInAppsCustomize) / 2;
-                previewPadding = new Point(padding, 0);
+            if (previewSizeBeforeScale[0] < icon.getWidth()) {
+                // The icon has extra padding around it.
+                int padding = (icon.getWidth() - previewSizeBeforeScale[0]) / 2;
+                if (icon.getWidth() > image.getWidth()) {
+                    padding = padding * image.getWidth() / icon.getWidth();
+                }
+
+                bounds.left += padding;
+                bounds.right -= padding;
             }
+            scale = bounds.width() / (float) preview.getWidth();
         } else {
             PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag();
             Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.activityInfo);
@@ -274,16 +270,12 @@
         boolean clipAlpha = !(createItemInfo instanceof PendingAddWidgetInfo &&
                 (((PendingAddWidgetInfo) createItemInfo).previewImage == 0));
 
-        // Save the preview for the outline generation, then dim the preview
-        outline = Bitmap.createScaledBitmap(preview, preview.getWidth(), preview.getHeight(),
-                false);
-
         // Start the drag
         mLauncher.lockScreenOrientation();
-        mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, outline, clipAlpha);
+        mLauncher.getWorkspace().onDragStartedWithItem(createItemInfo, preview, clipAlpha);
         mDragController.startDrag(image, preview, this, createItemInfo,
-                DragController.DRAG_ACTION_COPY, previewPadding, scale);
-        outline.recycle();
+                bounds, DragController.DRAG_ACTION_COPY, scale);
+
         preview.recycle();
         return true;
     }
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index d6e0628..2f733dc 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -15,10 +15,12 @@
  */
 package com.android.launcher3.widget;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.content.pm.ResolveInfo;
-import android.support.v7.widget.RecyclerView;
 import android.content.res.Resources;
+import android.os.Build;
+import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.RecyclerView.Adapter;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -29,10 +31,8 @@
 import android.widget.LinearLayout;
 
 import com.android.launcher3.BubbleTextView;
-
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DynamicGrid;
-import com.android.launcher3.IconCache;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
@@ -57,7 +57,6 @@
     private Context mContext;
     private Launcher mLauncher;
     private LayoutInflater mLayoutInflater;
-    private IconCache mIconCache;
 
     private WidgetsModel mWidgetsModel;
     private WidgetPreviewLoader mWidgetPreviewLoader;
@@ -77,9 +76,7 @@
 
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
-
         mLauncher = launcher;
-        mIconCache = LauncherAppState.getInstance().getIconCache();
 
         setContainerHeight();
     }
@@ -110,8 +107,7 @@
 
         if (diff > 0) {
             for (int i = 0; i < diff; i++) {
-                WidgetCell widget = new WidgetCell(mContext);
-                widget = (WidgetCell) mLayoutInflater.inflate(
+                WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
                         R.layout.widget_cell, row, false);
 
                 // set up touch.
@@ -136,27 +132,28 @@
         tv.applyFromPackageItemInfo(infoOut);
 
         // Bind the view in the widget horizontal tray region.
+        if (getWidgetPreviewLoader() == null) {
+            return;
+        }
         for (int i=0; i < infoList.size(); i++) {
             WidgetCell widget = (WidgetCell) row.getChildAt(i);
-            if (getWidgetPreviewLoader() == null) {
-                return;
-            }
             if (infoList.get(i) instanceof LauncherAppWidgetProviderInfo) {
                 LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) infoList.get(i);
                 PendingAddWidgetInfo pawi = new PendingAddWidgetInfo(info, null);
                 widget.setTag(pawi);
-                widget.applyFromAppWidgetProviderInfo(info, -1, mWidgetPreviewLoader);
+                widget.applyFromAppWidgetProviderInfo(info, mWidgetPreviewLoader);
             } else if (infoList.get(i) instanceof ResolveInfo) {
                 ResolveInfo info = (ResolveInfo) infoList.get(i);
                 PendingAddShortcutInfo pasi = new PendingAddShortcutInfo(info.activityInfo);
                 widget.setTag(pasi);
                 widget.applyFromResolveInfo(mLauncher.getPackageManager(), info, mWidgetPreviewLoader);
             }
-            widget.setVisibility(View.VISIBLE);
             widget.ensurePreview();
+            widget.setVisibility(View.VISIBLE);
         }
     }
 
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     @Override
     public WidgetsRowViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         if (DEBUG) {
@@ -167,7 +164,11 @@
                 R.layout.widgets_list_row_view, parent, false);
         LinearLayout cellList = (LinearLayout) container.findViewById(R.id.widgets_cell_list);
         MarginLayoutParams lp = (MarginLayoutParams) cellList.getLayoutParams();
-        lp.setMarginStart(mIndent);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            lp.setMarginStart(mIndent);
+        } else {
+            lp.leftMargin = mIndent;
+        }
         cellList.setLayoutParams(lp);
         return new WidgetsRowViewHolder(container);
     }
@@ -183,6 +184,15 @@
     }
 
     @Override
+    public boolean onFailedToRecycleView(WidgetsRowViewHolder holder) {
+        // If child views are animating, then the RecyclerView may choose not to recycle the view,
+        // causing extraneous onCreateViewHolder() calls.  It is safe in this case to continue
+        // recycling this view, and take care in onViewRecycled() to cancel any existing
+        // animations.
+        return true;
+    }
+
+    @Override
     public long getItemId(int pos) {
         return pos;
     }
diff --git a/src/com/android/launcher3/widget/WidgetsModel.java b/src/com/android/launcher3/widget/WidgetsModel.java
index 71a7b94..5a920e8 100644
--- a/src/com/android/launcher3/widget/WidgetsModel.java
+++ b/src/com/android/launcher3/widget/WidgetsModel.java
@@ -10,8 +10,9 @@
 import com.android.launcher3.IconCache;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.LauncherModel.WidgetAndShortcutNameComparator;
 import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.model.AppNameComparator;
+import com.android.launcher3.model.WidgetsAndShortcutNameComparator;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -40,12 +41,14 @@
     private RecyclerView.Adapter mAdapter;
 
     private Comparator mWidgetAndShortcutNameComparator;
+    private Comparator mAppNameComparator;
 
     private IconCache mIconCache;
 
     public WidgetsModel(Context context, RecyclerView.Adapter adapter) {
         mAdapter = adapter;
-        mWidgetAndShortcutNameComparator = new WidgetAndShortcutNameComparator(context);
+        mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context);
+        mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator();
         mIconCache = LauncherAppState.getInstance().getIconCache();
     }
 
@@ -108,7 +111,7 @@
         }
 
         // sort.
-        sortPackageItemInfos();
+        Collections.sort(mPackageItemInfos, mAppNameComparator);
         for (PackageItemInfo p: mPackageItemInfos) {
             Collections.sort(mWidgetsList.get(p), mWidgetAndShortcutNameComparator);
         }
@@ -116,13 +119,4 @@
         // notify.
         mAdapter.notifyDataSetChanged();
     }
-
-    private void sortPackageItemInfos() {
-        Collections.sort(mPackageItemInfos, new Comparator<PackageItemInfo>() {
-            @Override
-            public int compare(PackageItemInfo lhs, PackageItemInfo rhs) {
-                return lhs.title.toString().compareTo(rhs.title.toString());
-            }
-        });
-    }
 }
\ No newline at end of file