Merge "Fixing crash in restore" into ub-launcher3-burnaby
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f9f5fc2..5f9d7f4 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -70,7 +70,6 @@
     <uses-permission android:name="com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST" />
 
     <application
-        android:name="com.android.launcher3.LauncherApplication"
         android:allowBackup="@bool/enable_backup"
         android:backupAgent="com.android.launcher3.LauncherBackupAgentHelper"
         android:hardwareAccelerated="true"
diff --git a/WallpaperPicker/res/values-v21/styles.xml b/WallpaperPicker/res/values-v21/styles.xml
new file mode 100644
index 0000000..04f39de
--- /dev/null
+++ b/WallpaperPicker/res/values-v21/styles.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <style name="WallpaperCropperActionBar" parent="@android:style/Widget.DeviceDefault.ActionBar">
+        <item name="android:displayOptions">showCustom</item>
+        <item name="android:background">#88000000</item>
+        <item name="android:contentInsetEnd">0dp</item>
+        <item name="android:contentInsetLeft">0dp</item>
+        <item name="android:contentInsetRight">0dp</item>
+        <item name="android:contentInsetStart">0dp</item>
+    </style>
+
+</resources>
\ No newline at end of file
diff --git a/res/layout/all_apps_container.xml b/res/layout/all_apps_container.xml
index a20ab46..fc77cd3 100644
--- a/res/layout/all_apps_container.xml
+++ b/res/layout/all_apps_container.xml
@@ -38,6 +38,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/all_apps_search_bar_height"
+        android:paddingBottom="@dimen/all_apps_prediction_bar_bottom_padding"
         android:orientation="horizontal"
         android:descendantFocusability="afterDescendants"
         android:focusable="true"
diff --git a/res/layout/all_apps_icon.xml b/res/layout/all_apps_icon.xml
index 10ed25c..0985e95 100644
--- a/res/layout/all_apps_icon.xml
+++ b/res/layout/all_apps_icon.xml
@@ -20,7 +20,7 @@
     android:id="@+id/icon"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_gravity="left|center_vertical"
+    android:layout_gravity="center"
     android:paddingTop="@dimen/all_apps_icon_top_bottom_padding"
     android:paddingBottom="@dimen/all_apps_icon_top_bottom_padding"
     android:focusable="true"
diff --git a/res/layout/all_apps_prediction_bar_icon.xml b/res/layout/all_apps_prediction_bar_icon.xml
index 1e75d14..5f63f6b 100644
--- a/res/layout/all_apps_prediction_bar_icon.xml
+++ b/res/layout/all_apps_prediction_bar_icon.xml
@@ -19,7 +19,7 @@
     style="@style/Icon.AllApps"
     android:id="@+id/icon"
     android:layout_width="0dp"
-    android:layout_height="match_parent"
+    android:layout_height="wrap_content"
     android:layout_gravity="center"
     android:layout_weight="1"
     android:focusable="true"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 63edd88..2184ead 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -58,9 +58,10 @@
     <dimen name="all_apps_grid_section_y_offset">8dp</dimen>
     <dimen name="all_apps_grid_section_text_size">24sp</dimen>
     <dimen name="all_apps_search_bar_height">52dp</dimen>
+    <dimen name="all_apps_search_bar_prediction_bar_padding">8dp</dimen>
     <dimen name="all_apps_icon_top_bottom_padding">8dp</dimen>
-    <dimen name="all_apps_icon_left_right_padding">18dp</dimen>
-    <dimen name="all_apps_prediction_icon_top_bottom_padding">12dp</dimen>
+    <dimen name="all_apps_icon_width_gap">24dp</dimen>
+    <dimen name="all_apps_prediction_bar_bottom_padding">16dp</dimen>
 
     <dimen name="all_apps_fast_scroll_bar_width">4dp</dimen>
     <dimen name="all_apps_fast_scroll_scrubber_touch_inset">-16dp</dimen>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index f4e306a..6c13b4a 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -52,7 +52,6 @@
     private static final float SHADOW_Y_OFFSET = 2.0f;
     private static final int SHADOW_LARGE_COLOUR = 0xDD000000;
     private static final int SHADOW_SMALL_COLOUR = 0xCC000000;
-    static final float PADDING_V = 3.0f;
 
     private static final int DISPLAY_WORKSPACE = 0;
     private static final int DISPLAY_ALL_APPS = 1;
@@ -184,6 +183,12 @@
         verifyHighRes();
     }
 
+    /**
+     * Overrides the default long press timeout.
+     */
+    public void setLongPressTimeout(int longPressTimeout) {
+        mLongPressHelper.setLongPressTimeout(longPressTimeout);
+    }
 
     @Override
     protected boolean setFrame(int left, int top, int right, int bottom) {
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 09a71b0..b7f89d0 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -51,7 +51,6 @@
 
     protected Launcher mLauncher;
     private int mBottomDragPadding;
-    protected TextView mText;
     protected SearchDropTargetBar mSearchDropTargetBar;
 
     /** Whether this drop target is active for the current drag */
@@ -82,11 +81,9 @@
         mOriginalTextColor = getTextColors();
 
         // Remove the text in the Phone UI in landscape
-        int orientation = getResources().getConfiguration().orientation;
-        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            if (!LauncherAppState.getInstance().isScreenLarge()) {
-                setText("");
-            }
+        DeviceProfile grid = ((Launcher) getContext()).getDeviceProfile();
+        if (grid.isVerticalBarLayout()) {
+            setText("");
         }
     }
 
diff --git a/src/com/android/launcher3/CheckLongPressHelper.java b/src/com/android/launcher3/CheckLongPressHelper.java
index 381b678..483c622 100644
--- a/src/com/android/launcher3/CheckLongPressHelper.java
+++ b/src/com/android/launcher3/CheckLongPressHelper.java
@@ -21,9 +21,11 @@
 import com.android.launcher3.util.Thunk;
 
 public class CheckLongPressHelper {
+
     @Thunk View mView;
     @Thunk View.OnLongClickListener mListener;
     @Thunk boolean mHasPerformedLongPress;
+    private int mLongPressTimeout = 300;
     private CheckForLongPress mPendingCheckForLongPress;
 
     class CheckForLongPress implements Runnable {
@@ -53,14 +55,20 @@
         mListener = listener;
     }
 
+    /**
+     * Overrides the default long press timeout.
+     */
+    public void setLongPressTimeout(int longPressTimeout) {
+        mLongPressTimeout = longPressTimeout;
+    }
+
     public void postCheckForLongPress() {
         mHasPerformedLongPress = false;
 
         if (mPendingCheckForLongPress == null) {
             mPendingCheckForLongPress = new CheckForLongPress();
         }
-        mView.postDelayed(mPendingCheckForLongPress,
-                LauncherAppState.getInstance().getLongPressTimeout());
+        mView.postDelayed(mPendingCheckForLongPress, mLongPressTimeout);
     }
 
     public void cancelLongPress() {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 82be409..9c59dab 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -230,12 +230,12 @@
     public boolean updateAppsViewNumCols(Resources res, int containerWidth) {
         int appsViewLeftMarginPx =
                 res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
-        int allAppsCellPaddingPx =
-                res.getDimensionPixelSize(R.dimen.all_apps_icon_left_right_padding);
+        int allAppsCellWidthGap =
+                res.getDimensionPixelSize(R.dimen.all_apps_icon_width_gap);
         int availableAppsWidthPx = (containerWidth > 0) ? containerWidth : availableWidthPx;
         int numAppsCols = (availableAppsWidthPx - appsViewLeftMarginPx) /
-                (allAppsIconSizePx + 2 * allAppsCellPaddingPx);
-        int numPredictiveAppCols = isPhone ? 4 : numAppsCols;
+                (allAppsIconSizePx + allAppsCellWidthGap);
+        int numPredictiveAppCols = Math.max(inv.minAllAppsPredictionColumns, numAppsCols);
         if ((numAppsCols != allAppsNumCols) ||
                 (numPredictiveAppCols != allAppsNumPredictiveCols)) {
             allAppsNumCols = numAppsCols;
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index 7d90ba2..b7a5aa8 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -87,8 +87,8 @@
         LauncherAppState app = LauncherAppState.getInstance();
 
         InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
-        mMaxCountX = (int) profile.numFolderColumns;
-        mMaxCountY = (int) profile.numFolderRows;
+        mMaxCountX = profile.numFolderColumns;
+        mMaxCountY = profile.numFolderRows;
 
         mMaxItemsPerPage = mMaxCountX * mMaxCountY;
 
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index fcaf834..ae204c4 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -57,6 +57,11 @@
     public int numColumns;
 
     /**
+     * The minimum number of predicted apps in all apps.
+     */
+    int minAllAppsPredictionColumns;
+
+    /**
      * Number of icons per row and column in the folder.
      */
     public int numFolderRows;
@@ -84,11 +89,12 @@
 
     public InvariantDeviceProfile(InvariantDeviceProfile p) {
         this(p.name, p.minWidthDps, p.minHeightDps, p.numRows, p.numColumns,
-                p.numFolderRows, p.numFolderColumns, p.iconSize, p.iconTextSize, p.numHotseatIcons,
-                p.hotseatIconSize, p.defaultLayoutId);
+                p.numFolderRows, p.numFolderColumns, p.minAllAppsPredictionColumns,
+                p.iconSize, p.iconTextSize, p.numHotseatIcons, p.hotseatIconSize,
+                p.defaultLayoutId);
     }
 
-    InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc,
+    InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc, int maapc,
             float is, float its, float hs, float his, int dlId) {
         // Ensure that we have an odd number of hotseat items (since we need to place all apps)
         if (hs % 2 == 0) {
@@ -102,6 +108,7 @@
         numColumns = c;
         numFolderRows = fr;
         numFolderColumns = fc;
+        minAllAppsPredictionColumns = maapc;
         iconSize = is;
         iconTextSize = its;
         numHotseatIcons = hs;
@@ -137,6 +144,7 @@
         defaultLayoutId = closestProfile.defaultLayoutId;
         numFolderRows = closestProfile.numFolderRows;
         numFolderColumns = closestProfile.numFolderColumns;
+        minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns;
 
         iconSize = interpolatedDeviceProfileOut.iconSize;
         iconBitmapSize = Utilities.pxFromDp(iconSize, dm);
@@ -166,30 +174,30 @@
         // width, height, #rows, #columns, #folder rows, #folder columns,
         // iconSize, iconTextSize, #hotseat, #hotseatIconSize, defaultLayoutId.
         predefinedDeviceProfiles.add(new InvariantDeviceProfile("Super Short Stubby",
-                255, 300,     2, 3, 2, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
+                255, 300,     2, 3, 2, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
         predefinedDeviceProfiles.add(new InvariantDeviceProfile("Shorter Stubby",
-                255, 400,     3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
+                255, 400,     3, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
         predefinedDeviceProfiles.add(new InvariantDeviceProfile("Short Stubby",
-                275, 420,     3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
+                275, 420,     3, 4, 3, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
         predefinedDeviceProfiles.add(new InvariantDeviceProfile("Stubby",
-                255, 450,     3, 4, 3, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
+                255, 450,     3, 4, 3, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
         predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus S",
-                296, 491.33f, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
+                296, 491.33f, 4, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
         predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 4",
-                335, 567,     4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
+                335, 567,     4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
         predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5",
-                359, 567,     4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
+                359, 567,     4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
         predefinedDeviceProfiles.add(new InvariantDeviceProfile("Large Phone",
-                406, 694,     5, 5, 4, 4,  64, 14.4f,  5, 56, R.xml.default_workspace_5x5));
+                406, 694,     5, 5, 4, 4, 4, 64, 14.4f,  5, 56, R.xml.default_workspace_5x5));
         // The tablet profile is odd in that the landscape orientation
         // also includes the nav bar on the side
         predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 7",
-                575, 904,     5, 6, 4, 5, 72, 14.4f,  7, 60, R.xml.default_workspace_5x6));
+                575, 904,     5, 6, 4, 5, 4, 72, 14.4f,  7, 60, R.xml.default_workspace_5x6));
         // Larger tablet profiles always have system bars on the top & bottom
         predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 10",
-                727, 1207,    5, 6, 4, 5, 76, 14.4f,  7, 64, R.xml.default_workspace_5x6));
+                727, 1207,    5, 6, 4, 5, 4, 76, 14.4f,  7, 64, R.xml.default_workspace_5x6));
         predefinedDeviceProfiles.add(new InvariantDeviceProfile("20-inch Tablet",
-                1527, 2527,   7, 7, 6, 6, 100, 20,  7, 72, R.xml.default_workspace_4x4));
+                1527, 2527,   7, 7, 6, 6, 4, 100, 20,  7, 72, R.xml.default_workspace_4x4));
         return predefinedDeviceProfiles;
     }
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2be2d9d..076a6e6 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -465,7 +465,6 @@
                         app.getInvariantDeviceProfile().landscapeProfile
                             : app.getInvariantDeviceProfile().portraitProfile;
 
-        // the LauncherApplication should call this, but in case of Instrumentation it might not be present yet
         mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
                 Context.MODE_PRIVATE);
         mIsSafeModeEnabled = getPackageManager().isSafeMode();
@@ -2380,6 +2379,9 @@
         if (hostView != null) {
             appWidgetId = hostView.getAppWidgetId();
             addAppWidgetImpl(appWidgetId, info, hostView, info.info);
+
+            // Clear the boundWidget so that it doesn't get destroyed.
+            info.boundWidget = null;
         } else {
             // In this case, we either need to start an activity to get permission to bind
             // the widget, or we need to start an activity to configure the widget, or both.
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 0950f9f..76ad8c1 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -24,6 +24,7 @@
 import android.content.res.Resources;
 import android.util.Log;
 
+import android.view.ViewConfiguration;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.PackageInstallerCompat;
@@ -39,9 +40,6 @@
     private final IconCache mIconCache;
     private final WidgetPreviewLoader mWidgetCache;
 
-    private final boolean mIsScreenLarge;
-    private final int mLongPressTimeout = 300;
-
     private boolean mWallpaperChangedSinceLastCheck;
 
     private static WeakReference<LauncherProvider> sLauncherProvider;
@@ -85,8 +83,6 @@
             MemoryTracker.startTrackingMe(sContext, "L");
         }
 
-        // set sIsScreenXLarge and mScreenDensity *before* creating icon cache
-        mIsScreenLarge = isScreenLarge(sContext.getResources());
         mInvariantDeviceProfile = new InvariantDeviceProfile(sContext);
         mIconCache = new IconCache(sContext, mInvariantDeviceProfile);
         mWidgetCache = new WidgetPreviewLoader(sContext, mInvariantDeviceProfile, mIconCache);
@@ -148,6 +144,9 @@
         return mModel;
     }
 
+    /**
+     * TODO(winsonc, hyunyoungs): We need to respect this
+     */
     boolean shouldShowAppOrWidgetProvider(ComponentName componentName) {
         return mAppFilter == null || mAppFilter.shouldShowApp(componentName);
     }
@@ -167,20 +166,7 @@
     public WidgetPreviewLoader getWidgetCache() {
         return mWidgetCache;
     }
-
-    public boolean isScreenLarge() {
-        return mIsScreenLarge;
-    }
-
-    // Need a version that doesn't require an instance of LauncherAppState for the wallpaper picker
-    public static boolean isScreenLarge(Resources res) {
-        return res.getBoolean(R.bool.is_large_tablet);
-    }
-
-    public int getLongPressTimeout() {
-        return mLongPressTimeout;
-    }
-
+    
     public void onWallpaperChanged() {
         mWallpaperChangedSinceLastCheck = true;
     }
diff --git a/src/com/android/launcher3/LauncherApplication.java b/src/com/android/launcher3/LauncherApplication.java
deleted file mode 100644
index 8b179f1..0000000
--- a/src/com/android/launcher3/LauncherApplication.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.app.Application;
-
-public class LauncherApplication extends Application {
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        LauncherAppState.setApplicationContext(this);
-        LauncherAppState.getInstance();
-    }
-
-    @Override
-    public void onTerminate() {
-        super.onTerminate();
-        LauncherAppState.getInstance().onTerminate();
-    }
-}
\ No newline at end of file
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 10b8648..a132e91 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -45,6 +45,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.TransactionTooLargeException;
 import android.provider.BaseColumns;
 import android.text.TextUtils;
 import android.util.Log;
@@ -3284,27 +3285,51 @@
 
     public static List<LauncherAppWidgetProviderInfo> getWidgetProviders(Context context,
             boolean refresh) {
-        synchronized (sBgLock) {
-            if (sBgWidgetProviders == null || refresh) {
-                sBgWidgetProviders = new HashMap<>();
-                AppWidgetManagerCompat wm = AppWidgetManagerCompat.getInstance(context);
-                LauncherAppWidgetProviderInfo info;
+        ArrayList<LauncherAppWidgetProviderInfo> results =
+                new ArrayList<LauncherAppWidgetProviderInfo>();
+        try {
+            synchronized (sBgLock) {
+                if (sBgWidgetProviders == null || refresh) {
+                    HashMap<ComponentKey, LauncherAppWidgetProviderInfo> tmpWidgetProviders
+                            = new HashMap<>();
+                    AppWidgetManagerCompat wm = AppWidgetManagerCompat.getInstance(context);
+                    LauncherAppWidgetProviderInfo info;
 
-                List<AppWidgetProviderInfo> widgets = wm.getAllProviders();
-                for (AppWidgetProviderInfo pInfo : widgets) {
-                    info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo);
-                    UserHandleCompat user = wm.getUser(info);
-                    sBgWidgetProviders.put(new ComponentKey(info.provider, user), info);
-                }
+                    List<AppWidgetProviderInfo> widgets = wm.getAllProviders();
+                    for (AppWidgetProviderInfo pInfo : widgets) {
+                        info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo);
+                        UserHandleCompat user = wm.getUser(info);
+                        tmpWidgetProviders.put(new ComponentKey(info.provider, user), info);
+                    }
 
-                Collection<CustomAppWidget> customWidgets = Launcher.getCustomAppWidgets().values();
-                for (CustomAppWidget widget : customWidgets) {
-                    info = new LauncherAppWidgetProviderInfo(context, widget);
-                    UserHandleCompat user = wm.getUser(info);
-                    sBgWidgetProviders.put(new ComponentKey(info.provider, user), info);
+                    Collection<CustomAppWidget> customWidgets = Launcher.getCustomAppWidgets().values();
+                    for (CustomAppWidget widget : customWidgets) {
+                        info = new LauncherAppWidgetProviderInfo(context, widget);
+                        UserHandleCompat user = wm.getUser(info);
+                        tmpWidgetProviders.put(new ComponentKey(info.provider, user), info);
+                    }
+                    // Replace the global list at the very end, so that if there is an exception,
+                    // previously loaded provider list is used.
+                    sBgWidgetProviders = tmpWidgetProviders;
                 }
+                results.addAll(sBgWidgetProviders.values());
+                return results;
             }
-            return new ArrayList<LauncherAppWidgetProviderInfo>(sBgWidgetProviders.values());
+        } catch (Exception e) {
+            if (e.getCause() instanceof TransactionTooLargeException) {
+                // the returned value may be incomplete and will not be refreshed until the next
+                // time Launcher starts.
+                // TODO: after figuring out a repro step, introduce a dirty bit to check when
+                // onResume is called to refresh the widget provider list.
+                synchronized (sBgLock) {
+                    if (sBgWidgetProviders != null) {
+                        results.addAll(sBgWidgetProviders.values());
+                    }
+                    return results;
+                }
+            } else {
+                throw e;
+            }
         }
     }
 
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index b267f75..0cd980c 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -54,7 +54,6 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.widget.Toast;
-
 import junit.framework.Assert;
 
 import java.io.ByteArrayOutputStream;
@@ -628,6 +627,16 @@
         return m.replaceAll("$1");
     }
 
+    /**
+     * Calculates the height of a given string at a specific text size.
+     */
+    public static float calculateTextHeight(float textSizePx) {
+        Paint p = new Paint();
+        p.setTextSize(textSizePx);
+        Paint.FontMetrics fm = p.getFontMetrics();
+        return -fm.top + fm.bottom;
+    }
+
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     public static boolean isRtl(Resources res) {
         return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) &&
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index a621771..cfeced2 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -586,26 +586,26 @@
         protected Bitmap doInBackground(Void... params) {
             Bitmap unusedBitmap = null;
 
+            // If already cancelled before this gets to run in the background, then return early
+            if (isCancelled()) {
+                return null;
+            }
             synchronized (mUnusedBitmaps) {
-                // If already cancelled before this gets to run in the background, then return early
-                if (isCancelled()) {
-                    return null;
-                }
-                // Check if we can use a bitmap
+                // Check if we can re-use a bitmap
                 for (Bitmap candidate : mUnusedBitmaps) {
                     if (candidate != null && candidate.isMutable() &&
                             candidate.getWidth() == mPreviewWidth &&
                             candidate.getHeight() == mPreviewHeight) {
                         unusedBitmap = candidate;
+                        mUnusedBitmaps.remove(unusedBitmap);
                         break;
                     }
                 }
+            }
 
-                if (unusedBitmap == null) {
-                    unusedBitmap = Bitmap.createBitmap(mPreviewWidth, mPreviewHeight, Config.ARGB_8888);
-                } else {
-                    mUnusedBitmaps.remove(unusedBitmap);
-                }
+            // creating a bitmap is expensive. Do not do this inside synchronized block.
+            if (unusedBitmap == null) {
+                unusedBitmap = Bitmap.createBitmap(mPreviewWidth, mPreviewHeight, Config.ARGB_8888);
             }
             // If cancelled now, don't bother reading the preview from the DB
             if (isCancelled()) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 9386500..855a443 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -20,6 +20,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -239,11 +240,11 @@
         mLauncher = (Launcher) context;
         DeviceProfile grid = mLauncher.getDeviceProfile();
 
-        mContainerInset = context.getResources().getDimensionPixelSize(
-                R.dimen.all_apps_container_inset);
-        mPredictionBarHeight = grid.allAppsIconSizePx + grid.iconDrawablePaddingOriginalPx +
-                grid.allAppsIconTextSizePx +
-                2 * res.getDimensionPixelSize(R.dimen.all_apps_prediction_icon_top_bottom_padding);
+        mContainerInset = res.getDimensionPixelSize(R.dimen.all_apps_container_inset);
+        mPredictionBarHeight = (int) (grid.allAppsIconSizePx + grid.iconDrawablePaddingOriginalPx +
+                Utilities.calculateTextHeight(grid.allAppsIconTextSizePx) +
+                2 * res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding) +
+                res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_bottom_padding));
 
         mLayoutInflater = LayoutInflater.from(context);
 
@@ -432,6 +433,7 @@
                 icon = (BubbleTextView) mLayoutInflater.inflate(
                         R.layout.all_apps_prediction_bar_icon, mPredictionBarView, false);
                 icon.setFocusable(true);
+                icon.setLongPressTimeout(ViewConfiguration.get(getContext()).getLongPressTimeout());
                 mPredictionBarView.addView(icon);
             }
 
@@ -497,11 +499,11 @@
         int startMargin = grid.isPhone ? mContentMarginStart : 0;
         int inset = mFixedBounds.isEmpty() ? mContainerInset : mFixedBoundsContainerInset;
         if (isRtl) {
-            mAppsRecyclerView.setPadding(inset + mAppsRecyclerView.getScrollbarWidth(), inset,
-                    inset + startMargin, inset);
+            mAppsRecyclerView.setPadding(inset + mAppsRecyclerView.getScrollbarWidth(), 0,
+                    inset + startMargin, 0);
         } else {
-            mAppsRecyclerView.setPadding(inset + startMargin, inset,
-                    inset + mAppsRecyclerView.getScrollbarWidth(), inset);
+            mAppsRecyclerView.setPadding(inset + startMargin, 0,
+                    inset + mAppsRecyclerView.getScrollbarWidth(), 0);
         }
 
         // Update the header bar
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 307d940..4b8b2df 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -27,6 +27,7 @@
 import android.support.v7.widget.RecyclerView;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.widget.TextView;
 import com.android.launcher3.AppInfo;
@@ -139,7 +140,7 @@
 
                 if (shouldDrawItemDivider(holder, items) && !hasDrawnPredictedAppsDivider) {
                     // Draw the divider under the predicted apps
-                    int top = child.getTop() + child.getHeight();
+                    int top = child.getTop() + child.getHeight() - mPredictionBarBottomPadding / 2;
                     c.drawLine(mBackgroundPadding.left, top,
                             parent.getWidth() - mBackgroundPadding.right, top,
                             mPredictedAppsDividerPaint);
@@ -294,6 +295,7 @@
     private View.OnLongClickListener mIconLongClickListener;
     @Thunk final Rect mBackgroundPadding = new Rect();
     @Thunk int mPredictionBarHeight;
+    @Thunk int mPredictionBarBottomPadding;
     @Thunk int mAppsPerRow;
     @Thunk boolean mIsRtl;
     private String mEmptySearchText;
@@ -336,6 +338,8 @@
         mPredictedAppsDividerPaint.setStrokeWidth(Utilities.pxFromDp(1f, res.getDisplayMetrics()));
         mPredictedAppsDividerPaint.setColor(0x1E000000);
         mPredictedAppsDividerPaint.setAntiAlias(true);
+        mPredictionBarBottomPadding =
+                res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_bottom_padding);
     }
 
     /**
@@ -418,6 +422,8 @@
                 icon.setOnTouchListener(mTouchListener);
                 icon.setOnClickListener(mIconClickListener);
                 icon.setOnLongClickListener(mIconLongClickListener);
+                icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
+                        .getLongPressTimeout());
                 icon.setFocusable(true);
                 return new ViewHolder(icon);
             default:
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index 36cc2b1..88a6ca4 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -65,25 +65,6 @@
         return itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
     }
 
-    // Copy constructor
-    public PendingAddWidgetInfo(PendingAddWidgetInfo copy) {
-        minWidth = copy.minWidth;
-        minHeight = copy.minHeight;
-        minResizeWidth = copy.minResizeWidth;
-        minResizeHeight = copy.minResizeHeight;
-        previewImage = copy.previewImage;
-        icon = copy.icon;
-        info = copy.info;
-        boundWidget = copy.boundWidget;
-        componentName = copy.componentName;
-        itemType = copy.itemType;
-        spanX = copy.spanX;
-        spanY = copy.spanY;
-        minSpanX = copy.minSpanX;
-        minSpanY = copy.minSpanY;
-        bindOptions = copy.bindOptions == null ? null : (Bundle) copy.bindOptions.clone();
-    }
-
     @Override
     public String toString() {
         return String.format("PendingAddWidgetInfo package=%s, name=%s",
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 2714f51..7496ea2 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -26,6 +26,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnLayoutChangeListener;
+import android.view.ViewPropertyAnimator;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -175,7 +176,8 @@
         if (bitmap != null) {
             mWidgetImage.setBitmap(bitmap);
             mWidgetImage.setAlpha(0f);
-            mWidgetImage.animate().alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
+            ViewPropertyAnimator anim = mWidgetImage.animate();
+            anim.alpha(1.0f).setDuration(FADE_IN_DURATION_MS);
         }
     }
 
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 8875879..30b3d58 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -7,148 +7,111 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
-import android.util.Log;
 import android.view.View;
 
 import com.android.launcher3.AppWidgetResizeFrame;
+import com.android.launcher3.DragController.DragListener;
 import com.android.launcher3.DragLayer;
+import com.android.launcher3.DragSource;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
 import com.android.launcher3.util.Thunk;
 
-public class WidgetHostViewLoader {
-
-    private static final boolean DEBUG = false;
-    private static final String TAG = "WidgetHostViewLoader";
-
-    /* constants used for widget loading state. */
-    private static final int WIDGET_NO_CLEANUP_REQUIRED = -1;
-    private static final int WIDGET_PRELOAD_PENDING = 0;
-    private static final int WIDGET_BOUND = 1;
-    private static final int WIDGET_INFLATED = 2;
-
-    int mState = WIDGET_NO_CLEANUP_REQUIRED;
+public class WidgetHostViewLoader implements DragListener {
 
     /* Runnables to handle inflation and binding. */
-    private Runnable mInflateWidgetRunnable = null;
+    @Thunk Runnable mInflateWidgetRunnable = null;
     private Runnable mBindWidgetRunnable = null;
 
-    /* Id of the widget being handled. */
-    int mWidgetLoadingId = -1;
-    PendingAddWidgetInfo mCreateWidgetInfo = null;
-
     // TODO: technically, this class should not have to know the existence of the launcher.
     @Thunk Launcher mLauncher;
-    private Handler mHandler;
+    @Thunk Handler mHandler;
+    @Thunk final View mView;
+    @Thunk final PendingAddWidgetInfo mInfo;
 
-    public WidgetHostViewLoader(Launcher launcher) {
+    // Widget id generated for binding a widget host view or -1 for invalid id. The id is
+    // not is use as long as it is stored here and can be deleted safely. Once its used, this value
+    // to be set back to -1.
+    @Thunk int mWidgetLoadingId = -1;
+
+    public WidgetHostViewLoader(Launcher launcher, View view) {
         mLauncher = launcher;
         mHandler = new Handler();
+        mView = view;
+        mInfo = (PendingAddWidgetInfo) view.getTag();
+    }
+
+    @Override
+    public void onDragStart(DragSource source, Object info, int dragAction) { }
+
+    @Override
+    public void onDragEnd() {
+        // Cleanup up preloading state.
+        mLauncher.getDragController().removeDragListener(this);
+
+        mHandler.removeCallbacks(mBindWidgetRunnable);
+        mHandler.removeCallbacks(mInflateWidgetRunnable);
+
+        // Cleanup widget id
+        if (mWidgetLoadingId != -1) {
+            mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
+            mWidgetLoadingId = -1;
+        }
+
+        // The widget was inflated and added to the DragLayer -- remove it.
+        if (mInfo.boundWidget != null) {
+            mLauncher.getDragLayer().removeView(mInfo.boundWidget);
+            mLauncher.getAppWidgetHost().deleteAppWidgetId(mInfo.boundWidget.getAppWidgetId());
+            mInfo.boundWidget = null;
+        }
     }
 
     /**
-     * Start loading the widget.
+     * Start preloading the widget.
      */
-    public void load(View v) {
-        if (mCreateWidgetInfo != null) {
-            // Just in case the cleanup process wasn't properly executed.
-            finish(false);
+    public boolean preloadWidget() {
+        final LauncherAppWidgetProviderInfo pInfo = mInfo.info;
+
+        if (pInfo.isCustomWidget) {
+            return false;
         }
-        boolean status = false;
-        if (v.getTag() instanceof PendingAddWidgetInfo) {
-            mCreateWidgetInfo = new PendingAddWidgetInfo((PendingAddWidgetInfo) v.getTag());
-            status = preloadWidget(v, mCreateWidgetInfo);
-        }
-        if (DEBUG) {
-            Log.d(TAG, String.format("load started on [state=%d, status=%s]", mState, status));
-        }
-    }
-
-
-    /**
-     * Clean up according to what the last known state was.
-     * @param widgetIdUsed   {@code true} if the widgetId was consumed which can happen only
-     *                       when view is fully inflated
-     */
-    public void finish(boolean widgetIdUsed) {
-        if (DEBUG) {
-            Log.d(TAG, String.format("cancel on state [%d] widgetId=[%d]",
-                    mState, mWidgetLoadingId));
-        }
-
-        // If the widget was not added, we may need to do further cleanup.
-        PendingAddWidgetInfo info = mCreateWidgetInfo;
-        mCreateWidgetInfo = null;
-
-        if (mState == WIDGET_PRELOAD_PENDING) {
-            // We never did any preloading, so just remove pending callbacks to do so
-            mHandler.removeCallbacks(mBindWidgetRunnable);
-            mHandler.removeCallbacks(mInflateWidgetRunnable);
-        } else if (mState == WIDGET_BOUND) {
-             // Delete the widget id which was allocated
-            if (mWidgetLoadingId != -1 && !info.isCustomWidget()) {
-                mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
-            }
-
-            // We never got around to inflating the widget, so remove the callback to do so.
-            mHandler.removeCallbacks(mInflateWidgetRunnable);
-        } else if (mState == WIDGET_INFLATED && !widgetIdUsed) {
-            // Delete the widget id which was allocated
-            if (mWidgetLoadingId != -1 && !info.isCustomWidget()) {
-                mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
-            }
-
-            // The widget was inflated and added to the DragLayer -- remove it.
-            AppWidgetHostView widget = info.boundWidget;
-            mLauncher.getDragLayer().removeView(widget);
-        }
-        setState(WIDGET_NO_CLEANUP_REQUIRED);
-        mWidgetLoadingId = -1;
-    }
-
-    private boolean preloadWidget(final View v, final PendingAddWidgetInfo info) {
-        final LauncherAppWidgetProviderInfo pInfo = info.info;
-
-        final Bundle options = pInfo.isCustomWidget ? null :
-                getDefaultOptionsForWidget(mLauncher, info);
+        final Bundle options = getDefaultOptionsForWidget(mLauncher, mInfo);
 
         // If there is a configuration activity, do not follow thru bound and inflate.
         if (pInfo.configure != null) {
-            info.bindOptions = options;
+            mInfo.bindOptions = options;
             return false;
         }
-        setState(WIDGET_PRELOAD_PENDING);
+
         mBindWidgetRunnable = new Runnable() {
             @Override
             public void run() {
-                if (pInfo.isCustomWidget) {
-                    setState(WIDGET_BOUND);
-                    return;
-                }
-
                 mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
                 if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed(
                         mWidgetLoadingId, pInfo, options)) {
-                    setState(WIDGET_BOUND);
+
+                    // Widget id bound. Inflate the widget.
+                    mHandler.post(mInflateWidgetRunnable);
                 }
             }
         };
-        mHandler.post(mBindWidgetRunnable);
 
         mInflateWidgetRunnable = new Runnable() {
             @Override
             public void run() {
-                if (mState != WIDGET_BOUND) {
+                if (mWidgetLoadingId == -1) {
                     return;
                 }
                 AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView(
                         (Context) mLauncher, mWidgetLoadingId, pInfo);
-                info.boundWidget = hostView;
-                setState(WIDGET_INFLATED);
-                hostView.setVisibility(View.INVISIBLE);
-                int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(info, false);
+                mInfo.boundWidget = hostView;
 
+                // We used up the widget Id in binding the above view.
+                mWidgetLoadingId = -1;
+
+                hostView.setVisibility(View.INVISIBLE);
+                int[] unScaledSize = mLauncher.getWorkspace().estimateItemSize(mInfo, false);
                 // We want the first widget layout to be the correct size. This will be important
                 // for width size reporting to the AppWidgetManager.
                 DragLayer.LayoutParams lp = new DragLayer.LayoutParams(unScaledSize[0],
@@ -157,10 +120,11 @@
                 lp.customPosition = true;
                 hostView.setLayoutParams(lp);
                 mLauncher.getDragLayer().addView(hostView);
-                v.setTag(info);
+                mView.setTag(mInfo);
             }
         };
-        mHandler.post(mInflateWidgetRunnable);
+
+        mHandler.post(mBindWidgetRunnable);
         return true;
     }
 
@@ -188,11 +152,4 @@
         }
         return options;
     }
-
-    @Thunk void setState(int state) {
-        if (DEBUG) {
-            Log.d(TAG, String.format("     state [%d -> %d]", mState, state));
-        }
-        mState = state;
-    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetImageView.java b/src/com/android/launcher3/widget/WidgetImageView.java
index 6f8fd89..b0fbe1e 100644
--- a/src/com/android/launcher3/widget/WidgetImageView.java
+++ b/src/com/android/launcher3/widget/WidgetImageView.java
@@ -64,6 +64,14 @@
         }
     }
 
+    /**
+     * Prevents the inefficient alpha view rendering.
+     */
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
     private void updateDstRectF() {
         if (mBitmap.getWidth() > getWidth()) {
             float scale = ((float) getWidth()) / mBitmap.getWidth();
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 1184394..51f2a5f 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -74,7 +74,6 @@
 
     /* Rendering related. */
     private WidgetPreviewLoader mWidgetPreviewLoader;
-    private WidgetHostViewLoader mWidgetHostViewLoader;
 
     private Rect mPadding = new Rect();
 
@@ -90,7 +89,6 @@
         super(context, attrs, defStyleAttr);
         mLauncher = (Launcher) context;
         mDragController = mLauncher.getDragController();
-        mWidgetHostViewLoader = new WidgetHostViewLoader(mLauncher);
         mAdapter = new WidgetsListAdapter(context, this, this, mLauncher);
         mIconCache = (LauncherAppState.getInstance()).getIconCache();
         if (DEBUG) {
@@ -169,8 +167,13 @@
         if (!mLauncher.isDraggingEnabled()) return false;
 
         boolean status = beginDragging(v);
-        if (status) {
-            mWidgetHostViewLoader.load(v);
+        if (status && v.getTag() instanceof PendingAddWidgetInfo) {
+            WidgetHostViewLoader hostLoader = new WidgetHostViewLoader(mLauncher, v);
+            boolean preloadStatus = hostLoader.preloadWidget();
+            if (DEBUG) {
+                Log.d(TAG, String.format("preloading widget [status=%s]", preloadStatus));
+            }
+            mLauncher.getDragController().addDragListener(hostLoader);
         }
         return status;
     }
@@ -325,10 +328,6 @@
             }
             d.deferDragViewCleanupPostAnimation = false;
         }
-        //TODO(hyunyoungs): if drop fails, this call cleans up correctly.
-        // However, in rare corner case where drop succeeds but doesn't end up using the widget
-        // id created by the loader, this finish will leave dangling widget id.
-        mWidgetHostViewLoader.finish(success);
     }
 
     //
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
index 4aa3323..9d265f8 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -25,7 +25,6 @@
 
 import com.android.launcher3.BaseRecyclerView;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.AlphabeticIndexCompat;
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.PackageItemInfo;
 
@@ -37,7 +36,6 @@
     private static final String TAG = "WidgetsRecyclerView";
     private WidgetsModel mWidgets;
     private Rect mBackgroundPadding = new Rect();
-    private PackageItemInfo mLastPackageItemInfo;
 
     public WidgetsRecyclerView(Context context) {
         this(context, null);
@@ -48,9 +46,15 @@
     }
 
     public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        // API 21 and below only support 3 parameter ctor.
         super(context, attrs, defStyleAttr);
     }
 
+    public WidgetsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        this(context, attrs, defStyleAttr);
+    }
+
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();