Merge "Work profile badge alignment b/21336417" into ub-launcher3-burnaby
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f9f5fc2..03e9bbf 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -45,10 +45,6 @@
         android:protectionLevel="signature"
         />
     <permission
-        android:name="com.android.launcher3.permission.RECEIVE_UPDATE_ORIENTATION_BROADCASTS"
-        android:protectionLevel="signature"
-        />
-    <permission
         android:name="com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST"
         android:protectionLevel="signatureOrSystem" />
 
@@ -66,11 +62,9 @@
     <uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
     <uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />
     <uses-permission android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS" />
-    <uses-permission android:name="com.android.launcher3.permission.RECEIVE_UPDATE_ORIENTATION_BROADCASTS" />
     <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/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index 9415941..d9bfc30 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -1147,6 +1147,6 @@
             return true;
 
         // Check if the user has specifically enabled rotation via preferences.
-        return Utilities.isAllowRotationPrefEnabled(getApplicationContext());
+        return Utilities.isAllowRotationPrefEnabled(getApplicationContext(), true);
     }
 }
diff --git a/proguard.flags b/proguard.flags
index 5e2c384..7ec488b 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -44,7 +44,7 @@
   public void setBrightness(int);
 }
 
--keep class com.android.launcher3.AppsContainerRecyclerView {
+-keep class com.android.launcher3.BaseRecyclerView {
   public void setFastScrollerAlpha(float);
   public float getFastScrollerAlpha();
 }
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 305c310..440a537 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -27,9 +27,6 @@
     <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent -->
     <string name="receive_launch_broadcasts_permission" translatable="false">com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS</string>
 
-    <!-- Permission to receive the com.android.launcher3.SCREEN_ORIENTATION_PREF_CHANGED intent -->
-    <string name="receive_update_orientation_broadcasts_permission" translatable="false">com.android.launcher3.permission.RECEIVE_UPDATE_ORIENTATION_BROADCASTS</string>
-
     <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent -->
     <string name="receive_first_load_broadcast_permission" translatable="false">com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST</string>
 
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index a207d9a..3a741f2 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -290,6 +290,13 @@
     }
 
     /**
+     * Returns the fast scroller alpha.
+     */
+    public float getFastScrollerAlpha() {
+        return mFastScrollAlpha;
+    }
+
+    /**
      * Maps the touch (from 0..1) to the adapter position that should be visible.
      * <p>Override in each subclass of this base class.
      */
diff --git a/src/com/android/launcher3/ClickShadowView.java b/src/com/android/launcher3/ClickShadowView.java
index 42fafe2..e31d7f7 100644
--- a/src/com/android/launcher3/ClickShadowView.java
+++ b/src/com/android/launcher3/ClickShadowView.java
@@ -96,12 +96,14 @@
         float drawableWidth = view.getIcon().getBounds().width();
 
         setTranslationX(leftShift
+                + viewParent.getTranslationX()
                 + 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
+                + viewParent.getTranslationY()
                 + 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/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 0ede2fc..d93cdcc 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -16,11 +16,8 @@
 
 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;
 
 import com.android.launcher3.compat.UserHandleCompat;
@@ -70,18 +67,8 @@
         return source.supportsAppInfoDropTarget() && supportsDrop(getContext(), info);
     }
 
-    @SuppressWarnings("deprecation")
-    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
     public static boolean supportsDrop(Context context, Object info) {
-        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);
-        }
+        return info instanceof AppInfo || info instanceof PendingAddItemInfo;
     }
 
     @Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 2be2d9d..191fdf4 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -98,7 +98,6 @@
 
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.PagedView.PageSwitchListener;
-import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AppSearchManager;
 import com.android.launcher3.compat.AppWidgetManagerCompat;
@@ -399,29 +398,8 @@
     }
 
     private Stats mStats;
-
     FocusIndicatorView mFocusHandler;
-
-    @Thunk boolean mRotationEnabled = false;
-    private boolean mScreenOrientationSettingReceiverRegistered = false;
-
-    final private BroadcastReceiver mScreenOrientationSettingReceiver =
-            new BroadcastReceiver() {
-                @Thunk Runnable mUpdateOrientationRunnable = new Runnable() {
-                    public void run() {
-                        setOrientation();
-                    }
-                };
-
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    mRotationEnabled = intent.getBooleanExtra(
-                            Utilities.SCREEN_ROTATION_SETTING_EXTRA, false);
-                    if (!waitUntilResume(mUpdateOrientationRunnable, true)) {
-                        setOrientation();
-                    }
-                }
-            };
+    private boolean mRotationEnabled = false;
 
     @Thunk void setOrientation() {
         if (mRotationEnabled) {
@@ -432,6 +410,12 @@
         }
     }
 
+    private Runnable mUpdateOrientationRunnable = new Runnable() {
+        public void run() {
+            setOrientation();
+        }
+    };
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         if (DEBUG_STRICT_MODE) {
@@ -465,7 +449,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();
@@ -532,13 +515,7 @@
         // In case we are on a device with locked rotation, we should look at preferences to check
         // if the user has specifically allowed rotation.
         if (!mRotationEnabled) {
-            String updateOrientationBroadcastPermission = getResources().getString(
-                    R.string.receive_update_orientation_broadcasts_permission);
-            registerReceiver(mScreenOrientationSettingReceiver,
-                    new IntentFilter(Utilities.SCREEN_ROTATION_SETTING_INTENT),
-                    updateOrientationBroadcastPermission, null);
-            mScreenOrientationSettingReceiverRegistered = true;
-            mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext());
+            mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext(), false);
         }
 
         // On large interfaces, or on devices that a user has specifically enabled screen rotation,
@@ -564,6 +541,16 @@
         }
     }
 
+    @Override
+    public void onSettingsChanged(String settings, boolean value) {
+        if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(settings)) {
+            mRotationEnabled = value;
+            if (!waitUntilResume(mUpdateOrientationRunnable, true)) {
+                mUpdateOrientationRunnable.run();
+            }
+        }
+    }
+
     private LauncherCallbacks mLauncherCallbacks;
 
     public void onPostCreate(Bundle savedInstanceState) {
@@ -2032,11 +2019,6 @@
     public void onDestroy() {
         super.onDestroy();
 
-        if (mScreenOrientationSettingReceiverRegistered) {
-            unregisterReceiver(mScreenOrientationSettingReceiver);
-            mScreenOrientationSettingReceiverRegistered = false;
-        }
-
         // Remove all pending runnables
         mHandler.removeMessages(ADVANCE_MSG);
         mHandler.removeMessages(0);
@@ -2380,6 +2362,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.
@@ -2468,9 +2453,8 @@
             return;
         }
 
-        LauncherAccessibilityDelegate delegate =
-                LauncherAppState.getInstance().getAccessibilityDelegate();
-        if (delegate != null && delegate.onBackPressed()) {
+        if (mDragController.isDragging()) {
+            mDragController.cancelDrag();
             return;
         }
 
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 81d595a..540bdf8 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -21,11 +21,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.res.Configuration;
-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;
@@ -49,6 +46,7 @@
     private static LauncherAppState INSTANCE;
 
     private InvariantDeviceProfile mInvariantDeviceProfile;
+
     private LauncherAccessibilityDelegate mAccessibilityDelegate;
 
     public static LauncherAppState getInstance() {
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/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index b40ace3..dc0bccc 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -146,7 +146,6 @@
     private long mLastBackupRestoreTime;
     private boolean mBackupDataWasUpdated;
 
-    private LauncherAppState mLauncherAppState;
     private IconCache mIconCache;
     private DeviceProfieData mDeviceProfileData;
 
@@ -205,7 +204,11 @@
             return;
         }
 
-        lazyInitAppState(true /* noCreate */);
+        if (mDeviceProfileData == null) {
+            LauncherAppState app = LauncherAppState.getInstance();
+            mDeviceProfileData = initDeviceProfileData(app.getInvariantDeviceProfile());
+            mIconCache = app.getIconCache();
+        }
 
         Log.v(TAG, "lastBackupTime = " + in.t);
         mKeys.clear();
@@ -240,7 +243,7 @@
                 // Check if any metadata has changed
                 mBackupDataWasUpdated = (in.profile == null)
                         || !Arrays.equals(DeviceProfieData.toByteArray(in.profile),
-                            DeviceProfieData.toByteArray(getDeviceProfieData()))
+                            DeviceProfieData.toByteArray(mDeviceProfileData))
                         || (in.backupVersion != BACKUP_VERSION)
                         || (in.appVersion != getAppVersion());
             }
@@ -268,8 +271,7 @@
      * to this device.
      */
     private boolean isBackupCompatible(Journal oldState) {
-        DeviceProfieData currentProfile = getDeviceProfieData();
-
+        DeviceProfieData currentProfile = mDeviceProfileData;
         DeviceProfieData oldProfile = oldState.profile;
 
         if (oldProfile == null || oldProfile.desktopCols == 0) {
@@ -302,7 +304,14 @@
         if (!restoreSuccessful) {
             return;
         }
-        lazyInitAppState(false /* noCreate */);
+
+        if (mDeviceProfileData == null) {
+            // This call does not happen on a looper thread. So LauncherAppState
+            // can't be created . Instead initialize required dependencies directly.
+            InvariantDeviceProfile profile = new InvariantDeviceProfile(mContext);
+            mDeviceProfileData = initDeviceProfileData(profile);
+            mIconCache = new IconCache(mContext, profile);
+        }
 
         int dataSize = data.size();
         if (mBuffer.length < dataSize) {
@@ -379,7 +388,7 @@
         journal.key = mKeys.toArray(new BackupProtos.Key[mKeys.size()]);
         journal.appVersion = getAppVersion();
         journal.backupVersion = BACKUP_VERSION;
-        journal.profile = getDeviceProfieData();
+        journal.profile = mDeviceProfileData;
         return journal;
     }
 
@@ -392,31 +401,6 @@
         }
     }
 
-    /**
-     * @return the current device profile information.
-     */
-    private DeviceProfieData getDeviceProfieData() {
-        return mDeviceProfileData;
-    }
-
-    private void lazyInitAppState(boolean noCreate) {
-        if (mLauncherAppState != null) {
-            return;
-        }
-
-        if (noCreate) {
-            mLauncherAppState = LauncherAppState.getInstanceNoCreate();
-        } else {
-            LauncherAppState.setApplicationContext(mContext);
-            mLauncherAppState = LauncherAppState.getInstance();
-        }
-
-        mIconCache = mLauncherAppState.getIconCache();
-        InvariantDeviceProfile profile = mLauncherAppState.getInvariantDeviceProfile();
-
-        mDeviceProfileData = initDeviceProfileData(profile);
-    }
-
     private DeviceProfieData initDeviceProfileData(InvariantDeviceProfile profile) {
         DeviceProfieData data = new DeviceProfieData();
         data.desktopRows = profile.numRows;
@@ -631,7 +615,6 @@
     private void backupWidgets(BackupDataOutput data) throws IOException {
         // persist static widget info that hasn't been persisted yet
         final ContentResolver cr = mContext.getContentResolver();
-        final WidgetPreviewLoader previewLoader = mLauncherAppState.getWidgetCache();
         final int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
         int backupWidgetCount = 0;
 
@@ -644,7 +627,6 @@
             while(cursor.moveToNext()) {
                 final long id = cursor.getLong(ID_INDEX);
                 final String providerName = cursor.getString(APPWIDGET_PROVIDER_INDEX);
-                final int spanX = cursor.getInt(SPANX_INDEX);
                 final ComponentName provider = ComponentName.unflattenFromString(providerName);
                 Key key = null;
                 String backupKey = null;
@@ -664,9 +646,7 @@
                     if (backupWidgetCount < MAX_WIDGETS_PER_PASS) {
                         if (DEBUG) Log.d(TAG, "saving widget " + backupKey);
                         UserHandleCompat user = UserHandleCompat.myUserHandle();
-                        writeRowToBackup(key,
-                                packWidget(dpi, previewLoader, mIconCache, provider, user),
-                                data);
+                        writeRowToBackup(key, packWidget(dpi, provider, user), data);
                         mKeys.add(key);
                         backupWidgetCount ++;
                     } else {
@@ -895,7 +875,7 @@
                 UserManagerCompat.getInstance(mContext).getSerialNumberForUser(myUserHandle);
         values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber);
 
-        DeviceProfieData currentProfile = getDeviceProfieData();
+        DeviceProfieData currentProfile = mDeviceProfileData;
 
         if (favorite.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
             if (!TextUtils.isEmpty(favorite.appWidgetProvider)) {
@@ -972,8 +952,7 @@
     }
 
     /** Serialize a widget for persistence, including a checksum wrapper. */
-    private Widget packWidget(int dpi, WidgetPreviewLoader previewLoader, IconCache iconCache,
-            ComponentName provider, UserHandleCompat user) {
+    private Widget packWidget(int dpi, ComponentName provider, UserHandleCompat user) {
         final LauncherAppWidgetProviderInfo info =
                 LauncherModel.getProviderInfo(mContext, provider, user);
         Widget widget = new Widget();
@@ -982,7 +961,7 @@
         widget.configure = info.configure != null;
         if (info.icon != 0) {
             widget.icon = new Resource();
-            Drawable fullResIcon = iconCache.getFullResIcon(provider.getPackageName(), info.icon);
+            Drawable fullResIcon = mIconCache.getFullResIcon(provider.getPackageName(), info.icon);
             Bitmap icon = Utilities.createIconBitmap(fullResIcon, mContext);
             widget.icon.data = Utilities.flattenBitmap(icon);
             widget.icon.dpi = dpi;
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index ec4e4f9..c08cd0b 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -26,8 +26,6 @@
     public static final String WIDGET_PREVIEWS_DB = "widgetpreviews.db";
     public static final String APP_ICONS_DB = "app_icons.db";
 
-    public static final String ROTATION_PREF_FILE = "com.android.launcher3.rotation.prefs";
-
     public static final List<String> ALL_FILES = Collections.unmodifiableList(Arrays.asList(
             DEFAULT_WALLPAPER_THUMBNAIL,
             DEFAULT_WALLPAPER_THUMBNAIL_OLD,
@@ -37,8 +35,7 @@
             WALLPAPER_IMAGES_DB,
             WIDGET_PREVIEWS_DB,
             MANAGED_USER_PREFERENCES_KEY,
-            APP_ICONS_DB,
-            ROTATION_PREF_FILE));
+            APP_ICONS_DB));
 
     // TODO: Delete these files on upgrade
     public static final List<String> OBSOLETE_FILES = Collections.unmodifiableList(Arrays.asList(
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/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index b590126..cb808c2 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -39,8 +39,10 @@
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.database.sqlite.SQLiteStatement;
 import android.net.Uri;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Process;
 import android.os.StrictMode;
 import android.os.UserManager;
 import android.text.TextUtils;
@@ -237,6 +239,38 @@
         return count;
     }
 
+    @Override
+    public Bundle call(String method, String arg, Bundle extras) {
+        if (Binder.getCallingUid() != Process.myUid()) {
+            return null;
+        }
+
+        switch (method) {
+            case LauncherSettings.Settings.METHOD_GET_BOOLEAN: {
+                Bundle result = new Bundle();
+                result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
+                        getContext().getSharedPreferences(
+                                LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE)
+                                .getBoolean(arg, extras.getBoolean(
+                                        LauncherSettings.Settings.EXTRA_DEFAULT_VALUE)));
+                return result;
+            }
+            case LauncherSettings.Settings.METHOD_SET_BOOLEAN: {
+                boolean value = extras.getBoolean(LauncherSettings.Settings.EXTRA_VALUE);
+                getContext().getSharedPreferences(
+                        LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE)
+                        .edit().putBoolean(arg, value).apply();
+                if (mListener != null) {
+                    mListener.onSettingsChanged(arg, value);
+                }
+                Bundle result = new Bundle();
+                result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, value);
+                return result;
+            }
+        }
+        return null;
+    }
+
     private void notifyListeners() {
         // always notify the backup agent
         LauncherBackupAgentHelper.dataChanged(getContext());
diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java
index 0de96fb..5b5c6c5 100644
--- a/src/com/android/launcher3/LauncherProviderChangeListener.java
+++ b/src/com/android/launcher3/LauncherProviderChangeListener.java
@@ -8,4 +8,6 @@
 public interface LauncherProviderChangeListener {
 
     public void onLauncherProviderChange();
+
+    public void onSettingsChanged(String settings, boolean value);
 }
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 90e60e4..afdb3dd 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -305,4 +305,19 @@
          */
         static final String OPTIONS = "options";
     }
+
+    /**
+     * Launcher settings
+     */
+    public static final class Settings {
+
+        public static final Uri CONTENT_URI = Uri.parse("content://" +
+                ProviderConfig.AUTHORITY + "/settings");
+
+        public static final String METHOD_GET_BOOLEAN = "get_boolean_setting";
+        public static final String METHOD_SET_BOOLEAN = "set_boolean_setting";
+
+        public static final String EXTRA_VALUE = "value";
+        public static final String EXTRA_DEFAULT_VALUE = "default_value";
+    }
 }
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 18832c6..3d00034 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -567,6 +567,13 @@
     public void scrollTo(int x, int y) {
         // In free scroll mode, we clamp the scrollX
         if (mFreeScroll) {
+            // If the scroller is trying to move to a location beyond the maximum allowed
+            // in the free scroll mode, we make sure to end the scroll operation.
+            if (!mScroller.isFinished() &&
+                    (x > mFreeScrollMaxScrollX || x < mFreeScrollMinScrollX)) {
+                forceFinishScroller();
+            }
+
             x = Math.min(x, mFreeScrollMaxScrollX);
             x = Math.max(x, mFreeScrollMinScrollX);
         }
@@ -1038,7 +1045,7 @@
     public void removeViewAt(int index) {
         // XXX: We should find a better way to hook into this before the view
         // gets removed form its parent...
-        removeViewAt(index);
+        removeMarkerForView(index);
         super.removeViewAt(index);
     }
     @Override
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
index 27763f5..dab71c8 100644
--- a/src/com/android/launcher3/SettingsActivity.java
+++ b/src/com/android/launcher3/SettingsActivity.java
@@ -17,12 +17,11 @@
 package com.android.launcher3;
 
 import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
 import android.os.Bundle;
 import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
 import android.preference.PreferenceFragment;
-import android.preference.PreferenceScreen;
+import android.preference.SwitchPreference;
 
 /**
  * Settings activity for Launcher. Currently implements the following setting: Allow rotation
@@ -41,26 +40,36 @@
     /**
      * This fragment shows the launcher preferences.
      */
-    @SuppressWarnings("WeakerAccess")
-    public static class LauncherSettingsFragment extends PreferenceFragment {
+    public static class LauncherSettingsFragment extends PreferenceFragment
+            implements OnPreferenceChangeListener {
         @Override
         public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
-            getPreferenceManager().setSharedPreferencesMode(Context.MODE_MULTI_PROCESS);
-            getPreferenceManager().setSharedPreferencesName(LauncherFiles.ROTATION_PREF_FILE);
             addPreferencesFromResource(R.xml.launcher_preferences);
+
+            SwitchPreference pref = (SwitchPreference) findPreference(
+                    Utilities.ALLOW_ROTATION_PREFERENCE_KEY);
+            pref.setPersistent(false);
+
+            Bundle extras = new Bundle();
+            extras.putBoolean(LauncherSettings.Settings.EXTRA_DEFAULT_VALUE, false);
+            Bundle value = getActivity().getContentResolver().call(
+                    LauncherSettings.Settings.CONTENT_URI,
+                    LauncherSettings.Settings.METHOD_GET_BOOLEAN,
+                    Utilities.ALLOW_ROTATION_PREFERENCE_KEY, extras);
+            pref.setChecked(value.getBoolean(LauncherSettings.Settings.EXTRA_VALUE));
+
+            pref.setOnPreferenceChangeListener(this);
         }
 
         @Override
-        public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
-                                             Preference preference) {
-            boolean allowRotation = getPreferenceManager().getSharedPreferences().getBoolean(
-                    Utilities.ALLOW_ROTATION_PREFERENCE_KEY, false);
-            Intent rotationSetting = new Intent(Utilities.SCREEN_ROTATION_SETTING_INTENT);
-            String launchBroadcastPermission = getResources().getString(
-                            R.string.receive_update_orientation_broadcasts_permission);
-            rotationSetting.putExtra(Utilities.SCREEN_ROTATION_SETTING_EXTRA, allowRotation);
-            getActivity().sendBroadcast(rotationSetting, launchBroadcastPermission);
+        public boolean onPreferenceChange(Preference preference, Object newValue) {
+            Bundle extras = new Bundle();
+            extras.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, (Boolean) newValue);
+            getActivity().getContentResolver().call(
+                    LauncherSettings.Settings.CONTENT_URI,
+                    LauncherSettings.Settings.METHOD_SET_BOOLEAN,
+                    preference.getKey(), extras);
             return true;
         }
     }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 0cd980c..2d8a1b1 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -92,17 +92,15 @@
     private static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY);
 
     public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
-    public static final String SCREEN_ROTATION_SETTING_INTENT =
-            "come.android.launcher3.SCREEN_ORIENTATION_PREF_CHANGED";
-    public static final String SCREEN_ROTATION_SETTING_EXTRA = "screenRotationPref";
 
     public static boolean isPropertyEnabled(String propertyName) {
         return Log.isLoggable(propertyName, Log.VERBOSE);
     }
 
-    public static boolean isAllowRotationPrefEnabled(Context context) {
-        SharedPreferences sharedPrefs = context.getSharedPreferences(LauncherFiles.ROTATION_PREF_FILE,
-                Context.MODE_MULTI_PROCESS);
+    public static boolean isAllowRotationPrefEnabled(Context context, boolean multiProcess) {
+        SharedPreferences sharedPrefs = context.getSharedPreferences(
+                LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE | (multiProcess ?
+                        Context.MODE_MULTI_PROCESS : 0));
         boolean allowRotationPref = sharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false);
         return sForceEnableRotation || allowRotationPref;
     }
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 3c49ccc..fe7b25e 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -20,6 +20,8 @@
 import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeleteDropTarget;
+import com.android.launcher3.DragController.DragListener;
+import com.android.launcher3.DragSource;
 import com.android.launcher3.Folder;
 import com.android.launcher3.FolderInfo;
 import com.android.launcher3.InfoDropTarget;
@@ -39,7 +41,7 @@
 import java.util.ArrayList;
 
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-public class LauncherAccessibilityDelegate extends AccessibilityDelegate {
+public class LauncherAccessibilityDelegate extends AccessibilityDelegate implements DragListener {
 
     private static final String TAG = "LauncherAccessibilityDelegate";
 
@@ -328,7 +330,6 @@
         mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(clickedTarget, loc);
         mLauncher.getDragController().completeAccessibleDrag(loc);
 
-        endAccessibleDrag();
         if (!TextUtils.isEmpty(confirmation)) {
             announceConfirmation(confirmation);
         }
@@ -366,22 +367,21 @@
         }
         mDragSource.enableAccessibleDrag(true);
         mDragSource.startDrag(cellInfo, true);
-    }
 
-    public boolean onBackPressed() {
-        if (isInAccessibleDrag()) {
-            cancelAccessibleDrag();
-            return true;
+        if (mLauncher.getDragController().isDragging()) {
+            mLauncher.getDragController().addDragListener(this);
         }
-        return false;
     }
 
-    private void cancelAccessibleDrag() {
-        mLauncher.getDragController().cancelDrag();
-        endAccessibleDrag();
+
+    @Override
+    public void onDragStart(DragSource source, Object info, int dragAction) {
+        // No-op
     }
 
-    private void endAccessibleDrag() {
+    @Override
+    public void onDragEnd() {
+        mLauncher.getDragController().removeDragListener(this);
         mDragInfo = null;
         if (mDragSource != null) {
             mDragSource.enableAccessibleDrag(false);
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/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/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();